Детекция достижения конца списка

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


Подход к вычислению конца списка

Основная идея заключается в определении нижней границы контейнера Masonry относительно окна просмотра (viewport). Для этого используются методы JavaScript, позволяющие определить положение элемента на странице:

const grid = document.querySelector('.grid');
const lastItem = grid.lastElementChild;

const lastItemOffset = lastItem.offsetTop + lastItem.offsetHeight;
const pageOffset = window.pageYOffset + window.innerHeight;

Здесь:

  • lastItem.offsetTop — расстояние от верхнего края документа до верхнего края последнего элемента сетки.
  • lastItem.offsetHeight — высота последнего элемента.
  • window.pageYOffset — текущая вертикальная прокрутка страницы.
  • window.innerHeight — высота видимой области окна браузера.

Сравнение lastItemOffset и pageOffset позволяет определить, достиг ли пользователь конца списка:

if (pageOffset >= lastItemOffset) {
    // Достигнут конец списка
    loadMoreItems();
}

Обработка события прокрутки

Детекция конца списка обычно выполняется при событии scroll. Для оптимизации производительности используется throttle или debounce, чтобы не вызывать проверку слишком часто:

let isThrottled = false;

window.addEventListener('scroll', () => {
    if (isThrottled) return;
    isThrottled = true;

    setTimeout(() => {
        checkEndOfList();
        isThrottled = false;
    }, 200);
});

function checkEndOfList() {
    const lastItem = document.querySelector('.grid').lastElementChild;
    const lastItemOffset = lastItem.offsetTop + lastItem.offsetHeight;
    const pageOffset = window.pageYOffset + window.innerHeight;

    if (pageOffset >= lastItemOffset) {
        loadMoreItems();
    }
}

Принцип работы throttle: ограничение частоты вызова функции, предотвращающее перегрузку при активной прокрутке.


Поддержка Masonry при динамической подгрузке

После добавления новых элементов необходимо обновить Masonry, чтобы правильно рассчитать расположение плиток:

const msnry = new Masonry('.grid', {
    itemSelector: '.grid-item',
    columnWidth: '.grid-sizer',
    percentPosition: true
});

function loadMoreItems() {
    fetch('/api/items')
        .then(response => response.json())
        .then(data => {
            const fragment = document.createDocumentFragment();
            data.forEach(itemData => {
                const item = document.createElement('div');
                item.className = 'grid-item';
                item.innerHTML = itemData.content;
                fragment.appendChild(item);
            });

            const grid = document.querySelector('.grid');
            grid.appendChild(fragment);
            msnry.appended(fragment.children);
        });
}

Метод msnry.appended() сообщает Masonry о новых элементах и позволяет библиотеке корректно перестроить сетку без полной перерисовки.


Предварительная загрузка элементов

Для более плавного UX часто используют срабатывание загрузки до достижения конца списка, например, за 200–300 пикселей до низа. Это вычисляется путем добавления «буфера»:

const buffer = 300; // пиксели до конца
if (pageOffset + buffer >= lastItemOffset) {
    loadMoreItems();
}

Такой подход предотвращает паузы при скролле и делает интерфейс более отзывчивым.


Учет изменений размеров окна

При изменении размеров окна или ориентации устройства важно пересчитывать положение последнего элемента, так как сетка Masonry адаптивная:

window.addEventListener('resize', () => {
    checkEndOfList();
});

Masonry использует свой механизм layout() для перерасчета сетки, который можно вызывать после динамической подгрузки или изменения размеров:

msnry.layout();

Заключение по методам детекции

  • Основное сравнение pageOffset и lastItemOffset обеспечивает простую проверку достижения конца списка.
  • Использование throttle/debounce предотвращает перегрузку событий scroll.
  • Методы Masonry appended() и layout() поддерживают корректное отображение новых элементов.
  • Буферная загрузка позволяет улучшить UX при бесконечной прокрутке.
  • Пересчет сетки при изменении размеров окна гарантирует правильное выравнивание элементов.

Эти методы вместе образуют надежную систему детекции конца списка, позволяя создавать динамические и отзывчивые интерфейсы с Masonry.