У меня есть тонна изображений. Как сделать их ленивыми по требованию, когда они прокручиваются в поле зрения? - PullRequest
1 голос
/ 17 января 2020

Иногда я загружаю много HTML с сервера, либо как часть исходного документа, либо как AJAX. В других случаях я вставляю HTML динамически в документ.

Проблема в том, что этот HTML часто содержит много изображений. Например, в социальной сети у вас может быть множество аватаров пользователей с фотографиями. Это приводит к тоннам запросов к серверу.

Я знаю, что могу использовать HTTP / 2 для передачи этих запросов, но все же мне не нужно на самом деле получать все эти файлы с сервера, пока они не прокрутятся в Посмотреть. Как я могу это сделать?

1 Ответ

1 голос
/ 17 января 2020

Прежде всего, для любых значков, таких как кнопки и c., Вы должны загружать и использовать шрифт с CSS, или для большей гибкости, просто SVG . Тем не менее, вы по-прежнему будете иметь различные фотографии и пользовательский контент, который вы хотели бы отобразить на своей странице.

Благодаря новым замечательным функциям современного Интернета, теперь мы можем лениво загружать этих присосок ! Мы будем использовать API-интерфейс Intersection Observer и Object.defineProperty для переопределения различных способов, которыми изображение может попасть в документ во время выполнения.

Вот код, который сделает это за вас. Вы можете просто скопировать его в файл Javascript и включить этот файл в свой документ. Или вы можете также проанализировать это, узнать и рассказать мне, что я пропустил:

(function () {

var Elp = Element.prototype;

var observer = new IntersectionObserver(function (entries, observer) {
    entries.forEach(function (entry) {
        if (!entry.target || entry.target.tagName.toUpperCase() !== 'IMG') {
            return;
        }
        var img = entry.target;
        var rect = entry.intersectionRect;
        var src = img.getAttribute('data-defer-src');
        if (src && rect.width > 0 && rect.height > 0) {
            img.setAttribute('src', src);
            img.removeAttribute('data-defer-src');
        }
    });
}, {
    root: null, rootMargin: '0px', threshold: 0
});

// Observe whatever is on the page already

(function () {
    var imgs = document.body.getElementsByTagName('img');
    imgs = Array.from(imgs);
    imgs.forEach(function (img) {
        observer.observe(img);
    });
})();

// Override innerHTML

var originalSet = Object.getOwnPropertyDescriptor(Elp, 'innerHTML').set;
var originalGet = Object.getOwnPropertyDescriptor(Elp, 'innerHTML').get;

Object.defineProperty(Elp, 'innerHTML', {
    set: function (html) {
        var element = document.createElement('div');
        originalSet.call(element, html);
        var imgs = element.getElementsByTagName('img');
        var found = false;
        imgs = Array.from(imgs);
        imgs.forEach(function (img) {
            var src = img.getAttribute('src');
            if (src) {
                img.setAttribute('data-defer-src', src);
                img.removeAttribute('src');
                found = true;
            }
        });
        if (!found) {
            originalSet.call(this, html);
            return html;
        }
        originalSet.call(this, originalGet.call(element));
        var imgs2 = this.getElementsByTagName('img');
        imgs2 = Array.from(imgs);
        imgs2.forEach(function (img) {
            observer.observe(img);
        });
    },
    get: originalGet
});

// Override any ways to insert elements

['insertBefore', 'appendChild'].forEach(function (fn) {
    var orig = Elp[fn];
    Elp[fn] = function (element) {
        var imgs = null;
        if (!element) {
            return;
        }
        if (element.tagName && element.tagName.toUpperCase() === 'IMG') {
            imgs = [element];
        } else {
            imgs = element.getElementsByTagName('img');
        }
        var found = false;
        imgs.forEach(function (img) {
            var src = img.getAttribute('src');
            if (src) {
                img.setAttribute('data-defer-src', src);
                img.removeAttribute('src');
                observer.observe(img);
                found = true;
            }
        });
        return orig.apply(this, arguments);
    };
});

})();

Бонусные баллы, если вы можете настроить свой сервер для рендеринга <img data-defer-src="{{url here}}" alt="{{description here}}" title="{{title here}}"> вместо <img src="{{url here}}" alt="{{description here}}" title="{{title here}}">. Потому что нет надежного способа во всех браузерах перехватывать изображения после их вставки в DOM с помощью

...