Ленивая загрузка (lazy loading) — техника, при которой ресурсы подгружаются только в момент, когда они становятся необходимыми. В библиотеке Waypoints эта стратегия реализуется через отслеживание положения элемента относительно области просмотра и выполнение заданного обработчика при достижении определённой точки.
Ключевая идея: контент не загружается заранее, а инициируется событием прокрутки.
Waypoints отслеживает положение DOM-элементов и вызывает функции при пересечении заданной точки (offset). Для ленивой загрузки:
Простейшая схема:
new Waypoint({
element: document.getElementById('target'),
handler: function(direction) {
// загрузка контента
},
offset: '75%'
});
offset определяет момент срабатывания:
'75%' — когда элемент находится на 75% высоты
экрана'bottom-in-view' — когда элемент полностью
появляется<img data-src="image.jpg" class="lazy-image" alt="...">
Изображение не содержит src, чтобы не загружаться
сразу.
document.querySelectorAll('.lazy-image').forEach(function(img) {
new Waypoint({
element: img,
handler: function() {
img.src = img.dataset.src;
this.destroy();
},
offset: '100%'
});
});
data-src для хранения URLdestroy()), чтобы
избежать повторных вызовов'100%' означает загрузку прямо перед
появлениемWaypoints часто применяют для динамической подгрузки блоков страницы.
<div id="content-container"></div>
<div id="load-trigger"></div>
new Waypoint({
element: document.getElementById('load-trigger'),
handler: function() {
fetch('/api/content')
.then(response => response.text())
.then(html => {
document.getElementById('content-container')
.insertAdjacentHTML('beforeend', html);
});
this.destroy();
},
offset: '80%'
});
fetch для получения данныхinsertAdjacentHTMLОдин из наиболее распространённых сценариев — реализация бесконечной ленты.
let page = 1;
function loadMore() {
fetch(`/api/items?page=${page}`)
.then(res => res.json())
.then(data => {
const container = document.getElementById('list');
data.forEach(item => {
const div = document.createElement('div');
div.textContent = item.title;
container.appendChild(div);
});
page++;
});
}
const waypoint = new Waypoint({
element: document.getElementById('load-more'),
handler: function() {
loadMore();
},
offset: 'bottom-in-view'
});
let isLoading = false;
function loadMore() {
if (isLoading) return;
isLoading = true;
fetch(`/api/items?page=${page}`)
.then(res => res.json())
.then(data => {
// добавление элементов
page++;
isLoading = false;
});
}
Waypoints позволяет управлять множеством триггеров на странице.
Waypoint.refreshAll();
Используется после динамического добавления элементов.
document.querySelectorAll('.lazy-block').forEach(block => {
new Waypoint({
element: block,
handler: function() {
block.classList.add('loaded');
this.destroy();
},
offset: '70%'
});
});
Для повышения UX контент можно загружать заранее — до появления.
offset: '150%'
Контент начнёт загружаться ещё до попадания в область видимости.
new Waypoint({
element: section,
handler: function() {
loadSectionData();
this.destroy();
},
offset: '120%'
});
Каждый waypoint должен удаляться после использования:
this.destroy();
Waypoints уже оптимизирован, но при сложной логике стоит ограничивать вызовы:
function debounce(fn, delay) {
let timeout;
return function() {
clearTimeout(timeout);
timeout = setTimeout(fn, delay);
};
}
Загрузка контента часто сопровождается визуальными эффектами.
new Waypoint({
element: block,
handler: function() {
block.classList.add('fade-in');
this.destroy();
},
offset: '80%'
});
.fade-in {
opacity: 1;
transition: opacity 0.5s ease;
}
Важно учитывать сбои сети.
fetch('/api/data')
.then(res => {
if (!res.ok) throw new Error('Ошибка загрузки');
return res.json();
})
.catch(err => {
console.error(err);
});
Waypoints может использоваться вместе с:
$('#trigger').waypoint(function() {
$('#container').load('/content');
}, {
offset: '75%'
});
Перед загрузкой отображаются временные блоки:
<div class="placeholder"></div>
После загрузки заменяются реальным контентом.
Создаются стилизованные заглушки, имитирующие структуру:
.skeleton {
background: linear-gradient(90deg, #eee, #ddd, #eee);
}
Комбинация:
destroy() → утечки памятиДля крупных проектов логика загрузки выносится в отдельные модули:
class LazyLoader {
constructor(selector) {
this.elements = document.querySelectorAll(selector);
this.init();
}
init() {
this.elements.forEach(el => {
new Waypoint({
element: el,
handler: () => this.load(el),
offset: '80%'
});
});
}
load(el) {
el.src = el.dataset.src;
}
}