JavaScript: 2 интересных примера практического использования Intersection Observer API

от автора

Привет, друзья!

На днях мне посчастливилось заниматься решением 2 несложных, но довольно интересных задач на чистом JavaScript (из-за React чуть не забыл, как это делается). В процессе решения этих задач никто не пострадал, напротив, все остались довольны. Поэтому я решил поделиться результатами с сообществом.

Обратите внимание: данная статья рассчитана, преимущественно, на начинающих разработчиков и вряд ли покажется интересной опытным, хотя, смею надеяться, что и последние не пожалеют потраченного времени, если прочитают ее до конца.

Введение

Итак, задачи были следующими:

  • Реализовать навигацию по разделам сайта с визуальным переключением индикатора текущего местонахождения пользователя и возможностью переходить к определенному разделу по клику. Сама навигация согласно макету должна была выглядеть так:

  • Реализовать «ленивую» (отложенную, lazy) загрузку медиаресурсов (изображений, аудио и видео), поскольку те же изображения даже после сжатия с помощью gulp-imagemin весили (и весят, потому что они никуда не делись) неприличные 50 Мб и загружались при запуске приложения (больше они себя так не ведут).

Как я уже сказал, обе задачи надо было решить на ванильном JS. Почему? Потому что проект был, что называется, legacy — Python в лице Wagtail и JS в лице JQuery.

На первый взгляд, может показаться, что названные задачи являются совершенно разными и между ними не может быть ничего общего. “Что может их объединять?” — спросите вы.

Ответ: технология, которую можно использовать для их решения. Вы не ослышались, обе задачи можно решить с помощью одного инструмента и при этом его даже не придется устанавливать. Для решения задач нам не потребуются ни сторонние библиотеки, ни вспомогательные утилиты — интерфейс, о котором идет речь, предоставляется самим браузером.

Как вы уже могли догадаться по названию статьи, я говорю об Intersection Observer API (далее — IOA). Данный интерфейс поддерживается всеми современными браузерами.

Если кратко, то IOA позволяет асинхронно регистрировать момент пересечения целевого элемента с его предком, если таковой определен в настройках, или с областью видимости документа (viewport).

Экземпляр IOA создается так:

const observer = new IntersectionObserver(callback, options)

Конструктор IntersectionObserver принимает 2 параметра: функцию обратного вызова, запускаемую в момент пересечения — callback, и объект с настройками — options.

Объект с настройками может содержать 3 поля:

  • root — родительский элемент, за пересечением с которым (целевым элементом) наблюдает наблюдатель (извините за тавтологию). Если root не определен или имеет значение null, предком целевого элемента считается viewport
  • rootMargin — отступы вокруг root. Как и для CSS-свойства margin, значения для rooMargin могут задаваться в пикселях, процентах, em, rem и т.д., например, 20px 10px. По умолчанию данная настройка имеет значение 0
  • threshold — процент видимости целевого элемента от 0 до 1. Значение данной настройки может задаваться в виде числа или массива чисел. По умолчанию также имеет значение 0

Наблюдение за целевым элементом устанавливается посредством вызова метода observe:

observer.observe(target)

Существует также несколько других методов, но мы будет использовать только observe.

При достижении целевым элементом порогового значения (threshold) вызывается callback, который получает список объектов entries и самого наблюдателя. Каждый объект entry имеет несколько свойств (target, time, intersectionRatio и др.). Единственным свойством, которое нас интересует, является isIntersection — логическое значение, выступающее индикатором нахождения целевого элемента в зоне пересечения.

Кстати, процент пересечения также можно определять с помощью свойства intersectionRatio.

Пожалуй, это все, что нам нужно знать об IOA для того, чтобы успешно решить наши задачи. Приступаем.

Начнем с навигации.

Реализация навигации по разделам сайта

Давайте рассуждать.

Элементы меню (ссылки) соответствуют разделам страницы (заголовкам разделов, если быть точнее). Каждому заголовку соответствует определенная ссылка. Мы это знаем, но этого не знает JS. Поэтому нам нужно как-то сообщить ему об этих отношениях один к одному. Существует несколько способов это сделать, но самым простым является использование атрибута data-*. Назовем его data-section. Пускай для простоты значениями этих атрибутов будут номера разделов (1, 2, 3 и т.д.).

Также нам нужен какой-то способ визуального отображения текущего раздела, т.е. раздела, просматриваемого пользователем. Самым простым способом это сделать является добавление к такому разделу специального CSS-класса. Назовем этот класс active.

Вот как будет выглядеть один элемент навигации и соответствующий ему раздел:

<!-- элемент навигации --> <div class="step active" data-section="1">  <span class="text">Section 1</span>  <div class="dot"></div> </div>  <!-- раздел страницы, соответствующий этому элементу --> <section data-section="1">  <h2>Section 1</h2>  <p>Много lorem</p> </section>

Таких элементов и разделов у нас будет 5 штук.

Вся разметка:

<div id="page">  <nav aria-label="section navigation">    <div class="step active" data-section="1">      <span class="text">Section 1</span>      <div class="dot"></div>    </div>    <div class="line"></div>    <div class="step" data-section="2">      <span class="text">Section 2</span>      <div class="dot"></div>    </div>    <div class="line"></div>    <div class="step" data-section="3">      <span class="text">Section 3</span>      <div class="dot"></div>    </div>    <div class="line"></div>    <div class="step" data-section="4">      <span class="text">Section 4</span>      <div class="dot"></div>    </div>    <div class="line"></div>    <div class="step" data-section="5">      <span class="text">Section 5</span>      <div class="dot"></div>    </div>  </nav>   <main>    <section data-section="1">      <h2>Section 1</h2>      <p>        Lorem, ipsum dolor sit amet consectetur adipisicing elit. Sit        deserunt ea reiciendis sint ex recusandae repudiandae, ratione non        vero! Aliquam sed quis inventore aperiam quidem, velit illo        voluptates amet architecto aspernatur, et neque ipsam veritatis        itaque eius est vero illum officiis totam, a hic. Ex est quae vitae        itaque suscipit dolorem corporis assumenda exercitationem quos iure,        deserunt repellendus provident possimus, architecto iste mollitia        nemo dicta dolores ipsa labore deleniti doloremque? Sit omnis minima        quis voluptate sapiente rerum fugit totam ut illo reprehenderit        earum ratione temporibus sequi odio consectetur perspiciatis, soluta        quidem dicta aliquid. Totam, animi placeat quisquam ducimus        adipisci, velit ipsam alias inventore earum sit eveniet rem hic        labore repellendus, aut quam reprehenderit esse temporibus quas quo        laudantium fuga! Quidem tempore illum atque sit optio nisi, eius        reiciendis perferendis similique, voluptatum maxime qui ipsam        deleniti vitae quia vero ad consequatur aspernatur voluptatibus quo        facere? Consequuntur, non ipsa? Assumenda repellat impedit dolores        eius dolorem debitis eaque aspernatur deleniti optio atque, est iure        excepturi ullam odio, natus quos sit! Nostrum aspernatur quos esse        quisquam! Aut exercitationem aspernatur expedita quam ullam quas quo        dignissimos. Animi ex incidunt architecto, quaerat vel qui illum        ipsa dolores nesciunt sapiente suscipit odio facere corrupti.        Itaque, provident quis?      </p>    </section>    <section data-section="2">      <h2>Section 2</h2>      <p>        Lorem, ipsum dolor sit amet consectetur adipisicing elit. Sit        deserunt ea reiciendis sint ex recusandae repudiandae, ratione non        vero! Aliquam sed quis inventore aperiam quidem, velit illo        voluptates amet architecto aspernatur, et neque ipsam veritatis        itaque eius est vero illum officiis totam, a hic. Ex est quae vitae        itaque suscipit dolorem corporis assumenda exercitationem quos iure,        deserunt repellendus provident possimus, architecto iste mollitia        nemo dicta dolores ipsa labore deleniti doloremque? Sit omnis minima        quis voluptate sapiente rerum fugit totam ut illo reprehenderit        earum ratione temporibus sequi odio consectetur perspiciatis, soluta        quidem dicta aliquid. Totam, animi placeat quisquam ducimus        adipisci, velit ipsam alias inventore earum sit eveniet rem hic        labore repellendus, aut quam reprehenderit esse temporibus quas quo        laudantium fuga! Quidem tempore illum atque sit optio nisi, eius        reiciendis perferendis similique, voluptatum maxime qui ipsam        deleniti vitae quia vero ad consequatur aspernatur voluptatibus quo        facere? Consequuntur, non ipsa? Assumenda repellat impedit dolores        eius dolorem debitis eaque aspernatur deleniti optio atque, est iure        excepturi ullam odio, natus quos sit! Nostrum aspernatur quos esse        quisquam! Aut exercitationem aspernatur expedita quam ullam quas quo        dignissimos. Animi ex incidunt architecto, quaerat vel qui illum        ipsa dolores nesciunt sapiente suscipit odio facere corrupti.        Itaque, provident quis?      </p>    </section>    <section data-section="3">      <h2>Section 3</h2>      <p>        Lorem, ipsum dolor sit amet consectetur adipisicing elit. Sit        deserunt ea reiciendis sint ex recusandae repudiandae, ratione non        vero! Aliquam sed quis inventore aperiam quidem, velit illo        voluptates amet architecto aspernatur, et neque ipsam veritatis        itaque eius est vero illum officiis totam, a hic. Ex est quae vitae        itaque suscipit dolorem corporis assumenda exercitationem quos iure,        deserunt repellendus provident possimus, architecto iste mollitia        nemo dicta dolores ipsa labore deleniti doloremque? Sit omnis minima        quis voluptate sapiente rerum fugit totam ut illo reprehenderit        earum ratione temporibus sequi odio consectetur perspiciatis, soluta        quidem dicta aliquid. Totam, animi placeat quisquam ducimus        adipisci, velit ipsam alias inventore earum sit eveniet rem hic        labore repellendus, aut quam reprehenderit esse temporibus quas quo        laudantium fuga! Quidem tempore illum atque sit optio nisi, eius        reiciendis perferendis similique, voluptatum maxime qui ipsam        deleniti vitae quia vero ad consequatur aspernatur voluptatibus quo        facere? Consequuntur, non ipsa? Assumenda repellat impedit dolores        eius dolorem debitis eaque aspernatur deleniti optio atque, est iure        excepturi ullam odio, natus quos sit! Nostrum aspernatur quos esse        quisquam! Aut exercitationem aspernatur expedita quam ullam quas quo        dignissimos. Animi ex incidunt architecto, quaerat vel qui illum        ipsa dolores nesciunt sapiente suscipit odio facere corrupti.        Itaque, provident quis?      </p>    </section>    <section data-section="4">      <h2>Section 4</h2>      <p>        Lorem, ipsum dolor sit amet consectetur adipisicing elit. Sit        deserunt ea reiciendis sint ex recusandae repudiandae, ratione non        vero! Aliquam sed quis inventore aperiam quidem, velit illo        voluptates amet architecto aspernatur, et neque ipsam veritatis        itaque eius est vero illum officiis totam, a hic. Ex est quae vitae        itaque suscipit dolorem corporis assumenda exercitationem quos iure,        deserunt repellendus provident possimus, architecto iste mollitia        nemo dicta dolores ipsa labore deleniti doloremque? Sit omnis minima        quis voluptate sapiente rerum fugit totam ut illo reprehenderit        earum ratione temporibus sequi odio consectetur perspiciatis, soluta        quidem dicta aliquid. Totam, animi placeat quisquam ducimus        adipisci, velit ipsam alias inventore earum sit eveniet rem hic        labore repellendus, aut quam reprehenderit esse temporibus quas quo        laudantium fuga! Quidem tempore illum atque sit optio nisi, eius        reiciendis perferendis similique, voluptatum maxime qui ipsam        deleniti vitae quia vero ad consequatur aspernatur voluptatibus quo        facere? Consequuntur, non ipsa? Assumenda repellat impedit dolores        eius dolorem debitis eaque aspernatur deleniti optio atque, est iure        excepturi ullam odio, natus quos sit! Nostrum aspernatur quos esse        quisquam! Aut exercitationem aspernatur expedita quam ullam quas quo        dignissimos. Animi ex incidunt architecto, quaerat vel qui illum        ipsa dolores nesciunt sapiente suscipit odio facere corrupti.        Itaque, provident quis?      </p>    </section>    <section data-section="5">      <h2>Section 5</h2>      <p>        Lorem, ipsum dolor sit amet consectetur adipisicing elit. Sit        deserunt ea reiciendis sint ex recusandae repudiandae, ratione non        vero! Aliquam sed quis inventore aperiam quidem, velit illo        voluptates amet architecto aspernatur, et neque ipsam veritatis        itaque eius est vero illum officiis totam, a hic. Ex est quae vitae        itaque suscipit dolorem corporis assumenda exercitationem quos iure,        deserunt repellendus provident possimus, architecto iste mollitia        nemo dicta dolores ipsa labore deleniti doloremque? Sit omnis minima        quis voluptate sapiente rerum fugit totam ut illo reprehenderit        earum ratione temporibus sequi odio consectetur perspiciatis, soluta        quidem dicta aliquid. Totam, animi placeat quisquam ducimus        adipisci, velit ipsam alias inventore earum sit eveniet rem hic        labore repellendus, aut quam reprehenderit esse temporibus quas quo        laudantium fuga! Quidem tempore illum atque sit optio nisi, eius        reiciendis perferendis similique, voluptatum maxime qui ipsam        deleniti vitae quia vero ad consequatur aspernatur voluptatibus quo        facere? Consequuntur, non ipsa? Assumenda repellat impedit dolores        eius dolorem debitis eaque aspernatur deleniti optio atque, est iure        excepturi ullam odio, natus quos sit! Nostrum aspernatur quos esse        quisquam! Aut exercitationem aspernatur expedita quam ullam quas quo        dignissimos. Animi ex incidunt architecto, quaerat vel qui illum        ipsa dolores nesciunt sapiente suscipit odio facere corrupti.        Itaque, provident quis?      </p>    </section>  </main> </div>

Стили вполне себе обычные, так что на них я останавливаться не буду.

Все стили:

* {  margin: 0;  padding: 0;  box-sizing: border-box;  font-family: 'Montserrat', sans-serif;  font-size: 1rem; }  :root {  --main-light: #f0f0f0;  --main-dark: #3c3c3c;  --main-blue: #c5dfee;  --active-blue: #53acff; }  body {  background: var(--main-light);  color: var(--main-dark); }  #page {  max-width: 95%; }  nav {  display: flex;  flex-direction: column;  position: fixed;  top: 2rem;  right: 2rem;  width: 20%; }  main {  width: 80%;  padding: 1rem;  text-indent: 1.25rem; }  h2 {  font-size: 2rem;  margin: 1rem 0; }  p {  font-size: 1.1rem; }  .dot {  position: absolute;  width: 10px;  height: 10px;  right: 0;  top: 50%;  transform: translateY(-50%);  border-radius: 50%;  transition: 0.4s ease; }  .dot:after {  content: '';  position: absolute;  top: 50%;  right: 50%;  transform: translate(50%, -50%);  width: 10px;  height: 10px;  background-color: var(--main-blue);  border-radius: 50%;  transition: 0.4s ease; }  .active .dot {  width: 26px;  height: 26px;  border: 1px solid var(--active-blue);  right: -8px; }  .active .dot:after {  width: 16px;  height: 16px;  background-color: var(--active-blue); }  .step {  position: relative;  padding-right: 22px;  font-size: 16px;  display: flex;  align-items: center;  margin-left: auto;  transition: all 0.4s ease;  cursor: pointer;  user-select: none; }  .active .text {  color: var(--active-blue);  font-size: 20px;  margin: 10px 0 10px auto; }  .line {  height: 44px;  width: 1px;  background-color: var(--main-blue);  margin: -10px 4px -10px auto;  transition: all 0.4s ease; }

Как вы думаете, сколько строк JS-кода занимает решение данной задачи? Не забывайте о том, что кроме выделения элемента навигации, соответствующего текущему разделу, с помощью стилей, нам также необходимо реализовать переключение между разделами по клику (при этом визуализация должна отрабатывать корректно). Так сколько же? 100? 50?

Ответ: 30 строк! И это без экономии на пробелах и отступах.

Завернем весь наш код в функцию. Назовем ее createSectionNav. Пусть функция принимает корневой элемент — root:

// определяем функцию const createSectionNav = (root) => {  // ... } // вызываем ее с корневым элементом createSectionNav(document.querySelector('#page'))

Получаем ссылки на nav, main и текущий (активный) элемент навигации (далее — шаг):

const nav = root.querySelector('nav') const main = root.querySelector('main') let currentActiveStep = nav.querySelector('.active')

Сначала реализуем переключение между разделами по клику. Для прокрутки к определенному элементу мы воспользуемся глобальным методом scrollIntoView. А находить нужный элемент будем с помощью атрибута data-section:

nav.addEventListener('click', ({ target }) => {  // извлекаем значение атрибута `data-section`  const { section } = target.closest('.step').dataset   if (!section) return   // находим соответствующий элемент и выполняем к нему прокрутку  main.querySelector(`[data-section='${section}']`).scrollIntoView({    // плавно    behavior: 'smooth'  }) })

В принципе, если на странице много разделов, для того, чтобы каждый раз не искать нужный раздел с помощью querySelector, можно предварительно составить карту разделов:

const sectionMap = [...main.querySelectorAll('[data-section]')].reduce(  (obj, el) => {    obj[el.dataset.section] = el    return obj  },  {} )

И обращаться к соответствующему разделу по ключу:

sectionMap[section].scrollIntoView({  behavior: 'smooth' })

Это все, что требуется для переключения между разделами. “А как же визуальная составляющая?” — спросите вы. О, это самое интересное. Визуальная составляющая будет полностью инкапсулирована в наблюдателе.

Перебираем разделы:

main.querySelectorAll("section").forEach((s) => {  // ... }

Из настроек нам требуется только процент пересечения. Установим его в значение 0.3 (30%):

const options = { threshold: 0.3 }

Функция обратного вызова делает следующее:

  • принимает список объектов entries
  • извлекает первый entry
  • проверяет, находится ли он зоне пересечения
  • если находится
    • извлекает номер раздела из его data-section
    • удаляет класс active у текущего активного элемента
    • определяет новый активный элемент
    • добавляет к нему класс active
    • и присваивает новое значение переменной currentActiveStep

const callback = (entries) => {  if (entries[0].isIntersecting) {    const { section } = s.dataset    if (!section) return    currentActiveStep.classList.remove("active")    const newActiveStep = nav.querySelector(      `[data-section='${section}']`    )    newActiveStep.classList.add("active")    currentActiveStep = newActiveStep  } }

Создаем наблюдателя:

const observer = new IntersectionObserver(callback, options)

И начинаем следить за разделом:

observer.observe(s)

Если все это объединить, то получится следующее:

main.querySelectorAll("section").forEach((s) => {  new IntersectionObserver(    (entries) => {      if (entries[0].isIntersecting) {        const { section } = s.dataset        if (!section) return        currentActiveStep.classList.remove("active")        const newActiveStep = nav.querySelector(          `[data-section='${section}']`        )        newActiveStep.classList.add("active")        currentActiveStep = newActiveStep      }    },    {      threshold: 0.3    }  ).observe(s) })

Таким образом, визуальная составляющая полностью инкапсулирована в наблюдателе: колбэк запускается как при «ручной» прокрутке страницы, так и при клике по шагу — в этом случае выполняется автоматическая прокрутка, также приводящая к регистрации пересечения.

Поиграть с этим кодом можно здесь:

Теперь поговорим о ленивой загрузке.

Реализация ленивой загрузки медиаресурсов

“В чем проблема обычной загрузки медиаресурсов?” — спросите вы. Когда таких ресурсов немного, никакой проблемы нет. Но когда их много и когда компоненты приложения загружаются разом (в приложении не реализован code splitting — разделение кода на уровне компонентов), все ресурсы, используемые этими компонентами, загружаются вместе с ними. Большое количество ресурсов означает «много байт» полезной нагрузки, а много байт полезной нагрузки означает снижение производительности. Следовательно, чем больше весят загружаемые ресурсы, тем хуже производительность приложения.

Как только браузер при разборе разметки встречает атрибут src (например, <img src="https://example.com/some_img.png" alt="" role="presentation" />), он тут же загружает ресурс из указанного в атрибуте источника. Все ресурсы, загружаемые браузером при запуске приложения, можно найти в разделе Network (Сеть) инструментов разработчика (ресурсы можно фильтровать с помощью вкладок JS, CSS, Img и др.). Вот, например, Chrome загрузил аватар для моего Google-аккаунта:

Вы можете спросить: «Неужели браузеры не предоставляют какой-либо нативной технологии для реализации ленивой загрузки медиа?» В действительности, кое-что они для этого предоставляют, но… Конечно, есть но, иначе не было бы этой статьи.

Существует атрибут loading. Если добавить его к изображению или фрейму (iframe) со значением lazy, то они будут загружаться лениво. И все бы ничего, вот только данный атрибут не поддерживается Safari, а там, где поддерживается, срабатывает не всегда корректно, поэтому для продакшна он не подходит.

Загрузкой аудио и видео можно управлять с помощью атрибута preload. Если установить его в значение none, то медиа будет загружаться только после нажатия пользователем кнопки Play. Данный атрибут поддерживается всеми современными браузерами. Но его возможности являются довольно ограниченными.

Все-таки хотелось бы иметь какой-то общий подход к реализации ленивой загрузки медиа. Кроме того, основной вес большинства страниц составляют изображения.

Для того, чтобы избежать загрузки ресурсов при запуске приложения, необходимо избавиться от всех атрибутов src, но при этом иметь возможность загружать ресурсы… при пересечении соответствующих элементов с viewport! Здесь нам снова пригодится атрибут data-*. Заменим src на data-src:

<img data-src="https://images.unsplash.com/photo-1629290211578-213dc1f621e1" alt="" role="presentation" />

Разметка этого приложения будет похожа на разметку предыдущего, за исключением следующего:

  • нам не нужна панель навигации
  • нам хватит 3 разделов
  • между разделами будут находиться медиаресурсы: одно изображение, одно аудио и одно видео.

Вся разметка:

<main>  <section>    <h2>Section 1</h2>    <p>      Lorem ipsum dolor, sit amet consectetur adipisicing elit. Adipisci ut      corrupti deleniti reiciendis, consectetur tenetur libero voluptates,      repudiandae saepe, sit fugit? Eos expedita commodi maiores ipsum quo      cum, vel esse enim doloribus repudiandae beatae sint corporis amet      eaque aliquid, nostrum est. Labore rem incidunt perspiciatis qui      aliquid adipisci quasi, sapiente veritatis accusamus earum id animi      voluptas tempore, totam hic provident aut perferendis! Voluptate natus      enim quae necessitatibus provident at dolores? Soluta cum iusto facere      officia sunt dolores placeat, qui autem, esse nostrum quaerat, enim      quisquam. Error totam ducimus quos. Rem dolor dolorem nam in assumenda      ipsa nisi fuga voluptatibus officiis saepe, asperiores atque quasi      error dolore corrupti, quidem exercitationem? Consectetur a quis iusto      distinctio. Quis architecto quos doloremque mollitia ut nulla et      libero expedita? Suscipit, sint minus doloremque neque mollitia      aliquam ex accusantium deleniti ipsum laborum voluptas. Modi, incidunt      porro dolorum nobis sapiente, nulla rem vel dolores quisquam voluptas      pariatur nostrum ipsum obcaecati explicabo, assumenda minima      voluptatibus mollitia cum fugit eius temporibus officia voluptates?      Animi, explicabo officiis impedit vero quis facilis quia optio      sapiente exercitationem! Eum ad fugit velit pariatur sed cum      temporibus dolore magni illum quas repellendus quae hic vitae      deserunt, repellat nostrum quaerat ipsam, officia minima      necessitatibus reprehenderit. Alias repellat voluptatibus soluta vel      cupiditate fuga pariatur amet. Aperiam, at cum officia, eaque adipisci      id laudantium in repellendus doloribus iste exercitationem accusantium      facere ipsa omnis optio, ea neque nihil incidunt? Necessitatibus eum      laudantium deleniti sint vero vel possimus architecto, eaque quam      voluptate? Odio aliquam officiis sunt, harum fuga vitae neque,      obcaecati repellendus beatae et amet rerum facilis at ipsum unde      sapiente. Maxime rem quaerat iusto non dignissimos, eius tempora      ducimus, necessitatibus impedit eum, corporis voluptatibus alias sit      cupiditate. Error numquam, voluptatem quas et modi, eos, excepturi      animi explicabo aut cupiditate minus! Quos tempora, molestiae atque ea      adipisci porro optio.    </p>  </section>  <!-- https://caniuse.com/loading-lazy-attr -->  <div>    <!-- <img      data-src="https://images.unsplash.com/photo-1629290211578-213dc1f621e1"      alt=""      role="presentation"      loading="lazy"    /> -->    <img data-src="https://images.unsplash.com/photo-1629290211578-213dc1f621e1" alt="" role="presentation" />  </div>  <section>    <h2>Section 2</h2>    <p>      Lorem ipsum dolor sit amet, consectetur adipisicing elit. At, odit      nisi saepe, fuga cupiditate reprehenderit sequi ea dolore accusamus      incidunt quidem impedit? Adipisci magnam eveniet dolorum numquam ex      nam culpa exercitationem earum, sint tenetur doloribus dolore      recusandae unde at soluta, non libero accusamus hic esse doloremque      quibusdam? Architecto quo, aliquam ex id ullam eveniet laboriosam      tempora sapiente quas delectus recusandae alias, quidem accusantium      numquam blanditiis sit aspernatur, explicabo perspiciatis consectetur?      Provident, magnam possimus tenetur mollitia impedit atque nesciunt      voluptas aspernatur porro expedita culpa accusamus esse, non tempora      suscipit! Quos facilis, accusantium voluptate corporis dolorum      suscipit inventore minima cumque modi a deserunt reprehenderit nisi      officia molestias voluptatibus rerum saepe laudantium aperiam, non      eligendi ratione voluptas ducimus nemo! Mollitia qui quibusdam maiores      commodi libero perferendis iure, tenetur eaque? Ea, similique officia      illum dolor iusto enim! Ratione, voluptate! Possimus rerum ratione est      repellendus, asperiores quos quaerat ex at placeat ipsam assumenda      tempora, corrupti sint laborum consectetur non dolorum totam sed,      architecto quam. Nesciunt consequuntur rem necessitatibus ut beatae,      recusandae culpa sint maiores numquam nam cumque delectus eos facere      ab iste at, asperiores provident. Quo sunt nulla delectus officiis      enim sapiente illum, porro, eaque ut ipsum nostrum qui? Omnis est      consequatur pariatur labore odit maxime ea libero sit, veritatis      assumenda ducimus exercitationem laborum suscipit excepturi quis saepe      eaque illo aspernatur officiis inventore, commodi similique cupiditate      quos veniam. Natus distinctio voluptatibus accusamus officia corporis      cum esse magni a, repudiandae sed debitis sequi delectus, ex      accusantium. Illum voluptas eos aut aliquid facere unde. Ad      exercitationem quibusdam, temporibus dolorem nihil commodi soluta sunt      facilis aspernatur aperiam. Eaque expedita mollitia odio, in facilis      fuga aperiam neque incidunt quaerat id, exercitationem eum voluptas      nostrum quo soluta a dolores veritatis impedit dolor nihil placeat      itaque officiis! Quos consequuntur perspiciatis unde tenetur nemo quo      cumque, voluptas qui ex alias, nesciunt blanditiis.    </p>  </section>  <!-- https://caniuse.com/mdn-html_elements_audio_preload -->  <div>    <!-- <audio      src="https://www.bensound.com/bensound-music/bensound-happyrock.mp3"      controls      preload="none"    ></audio> -->    <audio data-src="https://www.bensound.com/bensound-music/bensound-happyrock.mp3" controls></audio>  </div>  <section>    <h2>Section 3</h2>    <p>      Lorem ipsum dolor sit amet consectetur, adipisicing elit. Libero quasi      architecto fuga voluptatum deleniti exercitationem, mollitia      similique, facere rem ad dolorum tenetur eaque a voluptas nobis      repellendus? Maxime est nemo placeat deleniti cumque modi ipsam hic      porro rem unde quae, dignissimos minima quam nesciunt temporibus      asperiores repudiandae voluptatibus in iure facere. Vitae itaque      numquam tempora! Eius accusantium excepturi ea omnis earum, minus fuga      sint totam iusto voluptatum, natus sit quidem! Deleniti nulla quia at      qui omnis accusamus nobis delectus ex, assumenda odio illum nemo ipsa      placeat quam vero, necessitatibus alias? Facilis ut explicabo iusto      atque corrupti similique nam temporibus totam neque molestiae. Saepe      esse veniam, iure deleniti modi vel voluptas rerum, assumenda fugit,      architecto ratione neque aliquid adipisci beatae vero tempora mollitia      ducimus perspiciatis maxime. Velit amet cupiditate harum nostrum modi      repellat unde error minus maxime inventore. Corrupti distinctio modi      ratione inventore maxime fugit voluptate dolore, itaque, cum fuga quia      pariatur deleniti sit possimus! Voluptas repudiandae alias earum quis      ipsa eveniet optio aliquid maxime placeat quae laboriosam eaque vitae      dicta impedit, adipisci ullam quas dolore architecto minima quibusdam      expedita accusantium eos explicabo? Iusto inventore dolorum ipsa      eligendi ea, totam nam sed et impedit similique quidem, esse non.      Dolore amet molestias commodi reiciendis nesciunt, nihil enim tempora      magni tenetur illo ex, consectetur labore, repellendus iusto at      laborum quae harum corporis corrupti? Rerum dolorum, velit laboriosam      eius vero dolorem voluptas laudantium, accusantium aspernatur esse      necessitatibus deleniti tempora et possimus, ut obcaecati nulla.      Ducimus sed amet magnam odio officiis fugit cum voluptatum expedita      placeat asperiores voluptas maxime, consequuntur ratione id provident?      Perspiciatis explicabo animi, ut incidunt reiciendis ad praesentium      asperiores atque distinctio quae. Illum numquam facilis veniam, quis      adipisci commodi iste voluptatum in quos libero, obcaecati mollitia.      Deleniti earum odit vitae saepe quos culpa nisi dicta accusantium      animi in dignissimos, neque alias a?    </p>  </section>  <!-- <video    src="https://assets.mixkit.co/videos/preview/mixkit-stars-in-space-1610-large.mp4"    controls    preload="none"  ></video> -->  <video data-src="https://assets.mixkit.co/videos/preview/mixkit-stars-in-space-1610-large.mp4" controls></video> </main>

И…

Все стили:

* {  margin: 0;  padding: 0;  box-sizing: border-box;  font-family: "Montserrat", sans-serif;  font-size: 1rem; }  :root {  --main-light: #f0f0f0;  --main-dark: #3c3c3c;  --main-blue: #c5dfee;  --active-blue: #53acff; }  body {  background: var(--main-light);  color: var(--main-dark); }  main {  margin: 0 auto;  padding: 1rem;  width: 90%;  text-indent: 1.25rem; }  h2 {  font-size: 2rem;  margin: 1rem 0; }  p {  font-size: 1.1rem; }  img {  width: 90%;  margin: 1rem auto; }  audio {  display: block;  margin: 1rem auto;  border-radius: 4px; }  audio:focus-visible {  outline: none; }  video {  width: 90%;  display: block;  margin: 1rem auto; }

Думаю, вы уже успели составить себе примерное представление о том, как будет выглядеть код нашего скрипта. Для демонстрации дополнительных возможностей IOA в сравнении с preload, предлагаю запускать автоматическое воспроизведение аудио и видео при пересечении ими порогового значения (которое мы также установим в 30%):

// создаем функцию const initLazyLoading = (root) => {  // находим все "ленивые" элементы  const lazyEls = [...root.querySelectorAll("[data-src]")]   // для каждого из них  lazyEls.forEach((el) => {    // создаем наблюдателя    new IntersectionObserver(      (entries) => {        // аудио или видео?        const audioOrVideo =          el.localName === "audio" || el.localName === "video"        // если целевой элемент находится в зоне пересечения        if (entries[0].isIntersecting) {          // извлекаем `src` из `data-src`          const { src } = el.dataset          if (!src) return          // если у элемента нет атрибута `src`          if (!el.hasAttribute("src")) {            // добавляем ему данный атрибут со значением `data-src`            el.setAttribute("src", src)            // удаляем `data-src`            el.removeAttribute("data-src")          }          if (audioOrVideo) {            // запускаем воспроизведение            el.play()          }        } else {          if (audioOrVideo) {            // останавливаем воспроизведение            el.pause()          }        }      },      {        // пороговое значение        threshold: 0.3      }      // начинаем наблюдать за элементом    ).observe(el)  }) } // вызываем функцию initLazyLoading(document.querySelector("main"))

Мы не снимаем наблюдение с элементов посредством вызова метода unobserve в колбэке, поскольку хотим запускать и останавливать воспроизведение аудио и видео при выполнении прокрутки страницы в обоих направлениях. Однако благодаря проверке if (!el.hasAttribute("src")) атрибут data-src меняется на атрибут src только один раз.

Поиграть с этим кодом можно здесь:

До тех пор, пока медиа находятся за пределами области просмотра (проигнорируем пороговое значение подобно константе в большом «O»), они загружаться не будут. В этом мы можем убедиться, изучив вкладку Network.

Изображение находится за пределами viewport:

А вот мы до него «докрутили»:

При попадании аудио и видео в область просмотра, начинается их автоматическое воспроизведение, которое автоматически останавливается при выходе соответствующих элементов из viewport.

Рекомендую взглянуть на библиотеку vanilla-lazyload. В дополнение к реализованному нами функционалу, она позволяет лениво загружать фоновые изображения, разные медиаресурсы в зависимости от ширины области просмотра (source и srcset) и т.д.

Вывод

В данной статье мы с вами рассмотрели всего лишь 2 примера практического использования IOA. На самом деле, возможности, предоставляемые данным интерфейсом, намного шире. Парочку идей можно найти в этой статье. А здесь можно почитать о других «наблюдателях».

Надеюсь, вы не зря потратили время и нашли для себя что-то интересное.

Благодарю за внимание и хорошего дня!



ссылка на оригинал статьи https://habr.com/ru/articles/574972/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *