Waypoints — это библиотека для работы с событиями скролла, позволяющая реагировать на появление элементов в видимой области окна браузера. Центральным понятием является Waypoint, объект, который привязывается к конкретному DOM-элементу и отслеживает его положение относительно окна или родительского контейнера. Важной частью работы Waypoints является адаптер, обеспечивающий совместимость с различными средами и упрощающий работу с DOM и событиями.
Адаптер — это интерфейс между Waypoints и конкретным способом доступа к DOM. Он скрывает различия между jQuery, Zepto или чистым JavaScript и позволяет Waypoints работать с любым из них без изменения внутренней логики. В Waypoints по умолчанию используются следующие адаптеры:
Каждый адаптер реализует одинаковый набор методов, что гарантирует унифицированное поведение Waypoints независимо от выбранной среды.
Все адаптеры поддерживают несколько ключевых методов, необходимых для работы Waypoints:
offset() Получение позиции элемента
относительно документа. Принимает на вход элемент и возвращает объект с
координатами top и left.
scrollTop() Получение текущей
вертикальной позиции скролла контейнера. Используется для вычисления,
достиг ли элемент нужной точки.
scrollLeft() Получение текущей
горизонтальной позиции скролла.
height() и width()
Определение размеров элемента или контейнера, что важно для вычисления
триггеров waypoint’ов.
bind() и unbind()
Привязка и снятие обработчиков событий скролла и изменения размеров.
Адаптер гарантирует корректную работу с различными механизмами событий в
jQuery, Zepto и нативном JS.
each() Итерация по коллекции
элементов для создания множества waypoint’ов одновременно.
extend() Объединение объектов с
опциями, позволяющее легко наследовать или переопределять параметры
waypoint’а.
Waypoints позволяет напрямую управлять адаптерами через глобальный
объект Waypoints.Adapter. Это даёт возможность:
Добавлять новые адаптеры
Waypoint.adapters.push({
name: 'CustomAdapter',
adapter: CustomAdapter
});Выбирать используемый адаптер для конкретного waypoint
var waypoint = new Waypoint({
element: document.getElementById('myElement'),
handler: function(direction) {
console.log('Waypoint достигнут:', direction);
},
context: window,
adapter: 'NativeAdapter'
});Переопределять методы существующих адаптеров для кастомизации поведения.
Каждый waypoint создаётся в определённом контексте — контейнере, относительно которого вычисляется позиция элемента. Адаптер отвечает за корректный расчёт позиции элемента и скролла внутри контекста, обеспечивая точное срабатывание waypoint’а. При использовании кастомного контекста важно удостовериться, что выбранный адаптер умеет работать с этим контейнером.
Пример привязки к кастомному контейнеру:
var waypoint = new Waypoint({
element: document.querySelector('.section'),
handler: function(dir) {
console.log('Секция достигнута', dir);
},
context: document.querySelector('.scroll-container'),
adapter: 'NativeAdapter'
});
Создание собственного адаптера может потребоваться, если стандартные jQuery, Zepto или NativeAdapter не удовлетворяют требованиям проекта. Основные шаги:
offset, scrollTop, scrollLeft,
height, width, bind,
unbind, each, extend.Waypoint.adapters.push().Пример минимального кастомного адаптера:
var CustomAdapter = {
offset: function(el) {
var rect = el.getBoundingClientRect();
return { top: rect.top + window.scrollY, left: rect.left + window.scrollX };
},
scrollTop: function() {
return window.scrollY || document.documentElement.scrollTop;
},
scrollLeft: function() {
return window.scrollX || document.documentElement.scrollLeft;
},
height: function(el) {
return el ? el.offsetHeight : window.innerHeight;
},
width: function(el) {
return el ? el.offsetWidth : window.innerWidth;
},
bind: function(el, event, handler) {
el.addEventListener(event, handler);
},
unbind: function(el, event, handler) {
el.removeEventListener(event, handler);
},
each: function(collection, callback) {
Array.prototype.forEach.call(collection, callback);
},
extend: function(dest, src) {
return Object.assign(dest, src);
}
};
Waypoint.adapters.push({ name: 'CustomAdapter', adapter: CustomAdapter });
overflow: scroll убедиться, что адаптер корректно вычисляет
позиции относительно этого блока.