Статья изначально была размещена в моем польском блоге — devszczepaniak.pl.

Если у вас есть опыт работы с JavaScript, вы, вероятно, использовали слушатели. Слушатели, как следует из названия, слушают определенное событие или события. После возникновения события они выполняют ранее определенные действия. Для тех, у кого никогда не было возможности использовать прослушиватели JavaScript, давайте взглянем на пример кода:

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

Точно так же работают веб-API Observer, предоставляемые браузерами. Прежде чем я начну описывать всех наблюдателей, мне нужно объяснить некоторые теории. Как видите, я до сих пор пишу о наблюдателях, наблюдениях и так далее. Эта концепция описывается и определяется в мире программирования как шаблон проектирования наблюдателя.

Несколько слов о наблюдателе

Как следует из названия, основным элементом этого шаблона является наблюдатель и наблюдаемые объекты. Это может показаться странным, но коротко: наблюдатель наблюдает за наблюдаемыми и выполняет заранее определенное действие, когда произойдет какое-то объявленное условие. Например, у нас по соседству живет надоедливая соседка, которая не любит детей. Этот сосед пытается прогнать детей каждый раз, когда дети играют в футбол рядом с его домом. Сосед — это наблюдатель, а дети — наблюдатели. Когда дети начинают играть в футбол, наблюдатель выполняет заранее заданное действие — пытается прогнать детей или вызвать полицию. Это зависит от того, насколько сосед раздражает 😄

Есть еще много вариантов использования этого шаблона в повседневной жизни. Если вы подписаны на канал на YouTube, вы можете щелкнуть значок колокольчика и получить уведомление, когда будет добавлено новое видео. Таким образом, вы создаете наблюдателя, который уведомляет вас, когда будет выполнено определенное действие — выпуск нового видео. Этот механизм также реализован практически в каждом мобильном приложении!

JavaScript-наблюдатели

Когда вы создаете приложение для браузера на JavaScript, предоставляется несколько веб-API наблюдателя:

  • Обозреватель мутаций
  • Обозреватель производительности
  • ResizeObserver
  • IntersectionObserver

MutationObserver

Как следует из названия, MutationObserver отслеживает мутации (изменения) наблюдаемого объекта. Чтобы прояснить это, давайте посмотрим на пример:

На первый взгляд, этот код не кажется сложным. Сначала создается новый наблюдатель, в котором мы объявляем действие, которое будет выполняться, когда событие будет наблюдаться. Затем вызывается метод observe(). Элемент с идентификатором foo добавляется в пул наблюдаемых объектов с некоторыми дополнительными параметрами, которые позволяют указать, например, дочерние элементы или атрибуты также должны быть соблюдены.

Первый вариант связан с изменением атрибутов. Примеры атрибутов: class, id, href, checked и т. д. Однако это не влияет на атрибуты data-value. Для атрибутов данных есть специальная опция — characterData. Другой вариант — childList, который прослушивает добавление или удаление дочерних элементов. И последнее, но не менее важное — это параметр subtree, который позволяет наблюдать за всеми дочерними элементами.

Здесь я должен указать, что эти параметры следует использовать осторожно. Например, если вы создадите наблюдатель изменений для элемента body, это может сильно повлиять на производительность. В этом случае каждое отдельное изменение в дереве DOM будет запускать определенное действие! Помимо этих параметров, можно установить и другие параметры:

  • attributeOldValue — определяет, следует ли передавать старое значение атрибута обратному вызову наблюдателя,
  • characterDataOldValue — то же самое, что и предыдущее, но относится к атрибутам данных,
  • attributeFilter — позволяет указать массив атрибутов для наблюдения. Нет смысла соблюдать все атрибуты, когда это не нужно.

Не забудьте отключить наблюдатель, когда он больше не нужен, с помощью метода observer.disconnect().

Обозреватель производительности

Я однажды упомянул фактор производительности в этой статье. Для измерения производительности можно использовать PerformanceObserver. В этом случае также может быть полезен кусок кода:

Как видите, создание других наблюдателей выглядит аналогично. В качестве аргумента передается обратный вызов с определенным действием. В методе observe() определяется список entryTypes. В этом случае обрабатывается только событие measure. Приведенный выше код измеряет, сколько времени займет добавление чисел от 0 до 50000. Каждое вхождение вызова performance.measure('sum') добавляется в массив entries. Затем легко вычислить разницу во времени между каждым событием. Полный список доступных entryTypes доступен на MDN.

ResizeObserver

Когда я впервые услышал о ResizeObserver, у меня были некоторые сомнения по этому поводу. Я могу использовать событие resize, которое генерируется в окне или при изменении размера документа. Изменил правила игры тот факт, что ResizeObserver позволяет прослушивать изменение размера любого элемента на веб-сайте.

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

Использование этого наблюдателя тривиально и нет никакой разницы по сравнению с другими. Мы просто создаем новый экземпляр и выполняем метод observe() с наблюдаемым элементом внутри.

IntersectionObserver

И последнее, но не менее важное — это IntersectionObserver. Вариант использования этого наблюдателя — когда вам нужно проверить, виден ли физически наблюдаемый элемент в представлении браузера. Его можно использовать, например, для функций отложенной загрузки или бесконечной прокрутки. Код этого наблюдателя идентичен предыдущему, но я все равно его добавлю:

Интересным свойством объекта entry является intersectionRatio, которое говорит, какой процент наблюдаемого объекта виден в представлении браузера.

Источники