Статья изначально была размещена в моем польском блоге — 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
, которое говорит, какой процент наблюдаемого объекта виден в представлении браузера.
Источники
- https://developer.mozilla.org/pl/docs/Web/Events
- https://davidwalsh.name/mutationobserver-api
- https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
- https://developer.mozilla.org/en-US/docs/Web/API/PerformanceObserver
- https://developer.mozilla.org/en-US/docs/Web/API/PerformanceEntry/entryType
- https://developers.google.com/web/updates/2016/06/performance-observer
- https://web.dev/resize-observer/
- https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver
- https://developers.google.com/web/updates/2016/04/intersectionobserver
- https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver
- https://hackernoon.com/observer-vs-pub-sub-pattern-50d3b27f838c