Accueil Kortic

    LazyLoad et Intersection Observer

    Utilisation de IntersectionObserver pour gérer le LazyLoad sur img picture iframe ou autres (à étendre si besoin).
    Permet de charger les contenus dans le cas où le navigateur ne gère pas IntersectionObserver.
    Ne pas oublier une petite balise noscript pour les navigateurs sans Javascript mais aussi pour le SEO.

    Le script

    ;(function ($) {

        'use strict';

        if(typeof DOM !== 'object') {

            const DOM = {
                document: $(document),
                body: $('body').first()
            };
        }


        let lazyClass = 'async',
            lazyClassLoaded = lazyClass + '-loaded',
            lazyAttributeData = 'srcset',
            lazyAttribute = 'data-' + lazyAttributeData,
            margins = {
                top: 0,
                right: 0,
                bottom: 100,
                left: 0
            };

        $.lazyIO = async function (fromDomNode) {

            if ("IntersectionObserver" in window) {

                const lazyLoad = async target => {

                    let target_ = $(target),
                        tag = target_.prop('tagName').toLowerCase();


                    // set attribute data-margins='{"bottom": 300, "left": 10, etc}' to specific margins instead of default 0 0 100 0
                    if(target_.attr('data-margins') !== undefined) {

                        let json = $.parseJSON(target_.attr('data-margins'));

                        for(let i in json) {
                            margins[i] = json[i];

                        }
                    }

                    let options = {
                        rootMargin: margins.top + 'px ' + margins.right + 'px ' + margins.bottom + 'px ' + margins.left + 'px',
                        threshold: [0.0]
                    };

                    let lookAtIo = new IntersectionObserver((entries, observer) => {

                            entries.forEach(entry => {

                                // load picture > source + img
                                if(tag === 'picture') {

                                    let picture_ = $(entry.target),
                                        img_ = picture_.find('img').first();

                                    if (entry.isIntersecting) {

                                        picture_.find('source').each( function () {
                                            let source_ = $(this);
                                            source_.attr(lazyAttributeData, source_.data(lazyAttributeData)).removeAttr(lazyAttribute);
                                        });

                                        img_.attr('src', img_.data(lazyAttributeData)).removeAttr(lazyAttribute);
                                        picture_.addClass(lazyClassLoaded);
                                        observer.disconnect();
                                    }

                                }

                                // load img
                                if($.inArray(tag , ['img', 'iframe']) !== -1) {

                                    if (entry.isIntersecting) {

                                        let img_ = $(entry.target);
                                        img_.attr('src', img_.data(lazyAttributeData)).addClass(lazyClassLoaded).removeAttr(lazyAttribute);
                                        observer.disconnect();

                                    }
                                }

                                // load xml Http Request
                                if($.inArray(tag , ['div', 'span', 'samp']) !== -1) {

                                    if (entry.isIntersecting) {

                                        let element_ = $(entry.target);

                                        $.get(element_.data(lazyAttributeData), response => {

                                            // set attribute data-type="" to specific response type (defatult HTML)

                                            switch (element_.attr('data-type')) {
                                                case 'json':
                                                    element_.text(JSON.stringify(response, null, '\t'));
                                                    break;

                                                // add other type if needed

                                                default:
                                                    element_.html(response);
                                            }


                                            element_.addClass(lazyClassLoaded).removeAttr(lazyAttribute);

                                            // on kortic.com (see JS sources or https://www.kortic.com/tooltip-infobulle-a11y-accessible.html)
                                            target_.find('[data-tooltip]').tooltip();

                                        });
                                        observer.disconnect();
                                    }
                                }

                                /* … optionally add other types of elements (asynchronous content, etc.) */

                            });

                    }, options);

                    lookAtIo.observe(target);
                };

                let elements = $('.' + lazyClass, fromDomNode === null ? DOM.body : fromDomNode);

                elements.length && elements.each( function () {
                    !$(this).hasClass(lazyClassLoaded) && lazyLoad(this);
                });

            } else {

                // fallback : charge tout si IntersectionObserver n'est pas pris en charge…
                $('.' + lazyClass, fromDomNode === null ? DOM.body : fromDomNode).each( async function() {

                    let element_ = $(this),
                        tag = element_.prop('tagName').toLowerCase();

                    // load picture > source + img
                    if(tag === 'picture') {
                        let img_ = element_.find('img').first();

                        element_.addClass(lazyClassLoaded).find('source').each( function () {
                            let source_ = $(this);
                            source_.attr(lazyAttributeData, source_.data(lazyAttributeData)).removeAttr(lazyAttribute);
                        });

                        img_.attr('src', img_.data(lazyAttributeData)).removeAttr(lazyAttribute);
                    }

                    // load img
                    if($.inArray(tag , ['img', 'iframe']) !== -1) {

                        element_.attr('src', element_.data(lazyAttributeData)).addClass(lazyClassLoaded).removeAttr(lazyAttribute);

                    }

                    // load xml Http Request
                    if($.inArray(tag , ['div', 'span', 'samp']) !== -1) {

                        element_.load(
                            element_.data(lazyAttributeData),
                            () => {
                                element_.addClass(lazyClassLoaded).removeAttr(lazyAttribute);

                                // on kortic.com (see JS sources or https://www.kortic.com/tooltip-infobulle-a11y-accessible.html)
                                element_.find('[data-tooltip]').tooltip();

                            }
                        );
                    }

                    /* … optionally add other types of elements (asynchronous content, etc.) */

                });
            }

        };

        DOM.document.ready( () => {
            $('.' + lazyClass).length && $.lazyIO();
        });

    })(jQuery);

    La CSS

    /* nécessite un svg animé (https://www.kortic.com/svg/xlink.svg par exemple) */

    html.no-js .async {
        display: none;
    }

    .async:not(.async-loaded):not(div):not(span) {
        background-image:url('/svg/spritesheet.svg#i-loader');
        background-size: 1.25rem 1.25rem;
        background-position: center center;
    }

    /* seulement pour Kortic :-) */
    html.dark-mode .async:not(.async-loaded):not(div):not(span) {
        background-image: url('/svg/spritesheet.svg#i-loader-light');
    }


    picture.async:not(.async-loaded) {
        max-width: 100%;
        min-width: 100%;
        min-height: 100%;
    }

    picture.async img, img.async {
        opacity: 0;
        -webkit-transition: opacity .5s ease-in .3s;
        -moz-transition: opacity .5s ease-in .3s;
        -ms-transition: opacity .5s ease-in .3s;
        transition: opacity .5s ease-in .3s;
    }

    picture.async.async-loaded img, img.async.async-loaded {
        opacity: 1;
    }

    Utilisation

    Tout est dans le js... Exemple ici

    Utilisation sur une image
    image Berlin-Potsdamer-Platz
    <img decoding="async"
        class="async"
        title="Berlin, Potsdamer Platz"
        alt="image Berlin-Potsdamer-Platz"
        src="data:image/gif;base64,R0lGODlhCgAKAIABAAAAAP///yH5BAEAAAEALAAAAAAKAAoAAAIIjI+py+0PYysAOw=="
        data-srcset="https://pictures.kortic.com/400x400/0066bb"
        />
    Utilisation sur picture
    <picture class="async">
        <source media="(max-width: 30rem)"  srcset="data:image/gif;base64,R0lGODlhCgAKAIABAAAAAP///yH5BAEAAAEALAAAAAAKAAoAAAIIjI+py+0PYysAOw==" data-srcset="https://pictures.kortic.com/400x400/0066bb" />
        <source media="(max-width: 63.9375rem)"  srcset="data:image/gif;base64,R0lGODlhCgAKAIABAAAAAP///yH5BAEAAAEALAAAAAAKAAoAAAIIjI+py+0PYysAOw==" data-srcset="https://pictures.kortic.com/200x200/0066bb" />
        <source media="(min-width: 64rem)"  srcset="data:image/gif;base64,R0lGODlhCgAKAIABAAAAAP///yH5BAEAAAEALAAAAAAKAAoAAAIIjI+py+0PYysAOw==" data-srcset="https://pictures.kortic.com/250x250/0066bb" />
        <img decoding="async" data-srcset="https://pictures.kortic.com/250x250/0066bb" src="data:image/gif;base64,R0lGODlhCgAKAIABAAAAAP///yH5BAEAAAEALAAAAAAKAAoAAAIIjI+py+0PYysAOw=="  alt="" />
    </picture>
    <noscript>
        <img src="https://pictures.kortic.com/400x400/0066bb" alt="" decoding="async" />
    </noscript>
    Si vous souhaitez participer, même modestement, au maintien du site et pour son usage, vous pouvez cliquer sur le joli bouton…
    Voir aussi…
    2020-03-28