Веб-компоненты — ленивая загрузка изображений с помощью Intersection Observer
В предыдущем посте Введение в веб-компоненты мы рассмотрели основы веб-компонентов и создали один автономный веб-компонент.
Теперь попробуем настроить существующий HTML-элемент и придайте ему новый вкус. Мы попытаемся создать настраиваемый элемент изображения <img>
. Давайте попробуем добавить функцию ленивой загрузки к существующему элементу HTML <img>
. Наш элемент <img>
будет откладывать загрузку с нужного src
до тех пор, пока он не станет видимым в окне просмотра. Проще говоря, элемент не будет загружать желаемый контент при загрузке страницы, а только тогда, когда он виден на экране.
Техника
Техника, используемая для ленивой загрузки изображения, состоит в том, что фиктивное изображение или изображение-заполнитель загружается при загрузке страницы. Нужное изображение загружается только тогда, когда элемент виден в окне просмотра. Для этого мы должны цепляться за обработчики событий scroll или resize. Хотя это прекрасно работает, современные браузеры предлагают более эффективные и эффективные способы определения того, находится ли элемент в области просмотра с помощью Intersection Observer API.
API-интерфейс наблюдателя за пересечением
Intersection Observer API предоставляет способ асинхронно наблюдать за изменениями в пересечении целевого элемента с элементом-предком или с областью просмотра документа верхнего уровня. Его можно использовать в различных местах, таких как отложенная загрузка изображения, анимация, бесконечная прокрутка и т. д.
использование
/* declare the options */ const options = { root: null, rootMargin: '0px', threshold: [0.50] }; /* create an instance and pass the callback and options */ const observer = new IntersectionObserver(callback, options); /* finally observe the target element */ observer.observe(document.querySelector('#elem-to-watch-for'));
Параметры наблюдателя перекрестка
- root: элемент-предок, используемый в качестве области просмотра для проверки видимости целевого элемента. По умолчанию используется область просмотра браузера, если она не указана или равна нулю.
- rootMargin: поле вокруг корня. Можно указать аналогично свойству поля CSS (верхнее, правое, нижнее, левое). Это позволяет вам увеличивать или уменьшать площадь, используемую для пересечений. По умолчанию нули.
- threshold: позволяет определить массив пороговых значений intersectionRatio. Ваш обратный вызов будет вызываться каждый раз, когда intersectionRatio пересекает одно из этих значений. Значение по умолчанию для threshold — [0].
Обратный вызов наблюдателя пересечения
Обратный вызов вызывается каждый раз, когда цель достигает порога. Обратный вызов получает список IntersectionObserverEntry
объектов и наблюдателя.
const callback = (entries, observer) => { // each entry in the entries array corresponds to one observed //target entries.forEach(entry => { // entry.boundingClientRect // entry.intersectionRatio // entry.intersectionRect // entry.isIntersecting // entry.rootBounds // entry.target // entry.time }); };
Итак, давайте воспользуемся Intersection Observer API для создания веб-компонента, который по сути наследуется от HTMLImageElement
и помогает нам лениво загружать нужное изображение.
Как объяснялось выше, нам нужно знать URL-адрес изображения-заполнителя и URL-адрес желаемого изображения. Итак, мы запросим у пользователя эти два значения атрибута:
src: (необязательно) укажите URL-адрес изображения-заполнителя для загрузки заранее. (В качестве альтернативы добавьте URL-адрес заполнителя, если значение не указано пользователем).
data-src: (обязательно) укажите URL нужного изображения.
Шаги
- Создайте класс (например, LazyImg) и расширьте его из HTMLImageElement.
- Определите параметры, которые будут использоваться в Interserction Observer API.
- Укажите резервный URL-адрес заполнителя.
- Начните наблюдать за элементом
<img>
/* customised-img.js */ class LazyImg extends HTMLImageElement { constructor() { self = super(); // declare Intersection Observer options const options = { root: null, rootMargin: '0px', threshold: [0.50] // invoke the callback as soon as the element is 50% visible }; // add fallback placeholder url if (!(self.getAttribute('src'))) { self.setAttribute( 'src', 'https://i.picsum.photos/id/208/20/30.jpg?blur=5'); } // instantiate the observer const observer = new IntersectionObserver( self.handleCallback.bind(self), options); // start observing observer.observe(self); } }
5. Добавьте функцию обратного вызова
6. Зарегистрируйте веб-компонент.
/* customised-img.js */ handleCallback(entries, observer) { entries.forEach((entry, index) => { const target = entry.target; const dataSrc = target.getAttribute('data-src'); const src = target.getAttribute('src'); if (entry.isIntersecting) { if (src !== dataSrc) { self.loadImage(observer, target, dataSrc); } } }); } loadImage(observer, target, dataSrc) { // put the desired url as the src target.setAttribute('src', dataSrc); // add a class lazy-loaded target.setAttribute( 'class', `${target.getAttribute('class')} lazy-loaded`); // unobserve the element observer.unobserve(target); } /* finally register the web component */ customElements.define('lazy-img', LazyImg, { extends: 'img' });
Теперь наш веб-компонент готов.
<img is="lazy-img" src="placeholder-url" data-src="desired-url" />
Давайте задействуем его.
/* index.html */ <body> <div id="dashboard"> <ul id="dashboard-list"> <li class="item"> <img is="lazy-img" class="my-image" src="https://i.picsum.photos/id/500/20/30.jpg?blur=3" data-src="https://i.picsum.photos/id/500/200/300.jpg" alt="my-image"/> </li> <li class="item"> <img is="lazy-img" class="my-image" src="https://i.picsum.photos/id/501/20/30.jpg?blur=3" data-src="https://i.picsum.photos/id/501/200/300.jpg" alt="my-image"/> </li> <li class="item"> <img is="lazy-img" class="my-image" src="https://i.picsum.photos/id/602/20/30.jpg?blur=3" data-src="https://i.picsum.photos/id/602/200/300.jpg" alt="my-image"/> </li> <li class="item"> <img is="lazy-img" class="my-image" src="https://i.picsum.photos/id/603/20/30.jpg?blur=3" data-src="https://i.picsum.photos/id/603/200/300.jpg" alt="my-image"/> </li> </ul> </div> <script src="customised-img.js"></script> </body>
Вуаля! Оно работает!
Большое спасибо Lorem Picsum за все красивые и настраиваемые изображения на ходу!