Работа с динамическим контентом

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

Механизм перерасчёта макета основан на повторной компоновке элементов внутри контейнера. Packery отслеживает размеры элементов и их доступное пространство, размещая блоки по принципу «упаковки» (bin-packing).

Типичная инициализация:

var grid = document.querySelector('.grid');

var pckry = new Packery(grid, {
  itemSelector: '.grid-item',
  gutter: 10
});

Контейнер .grid управляет позиционированием всех элементов .grid-item. После инициализации сетка может изменяться программно.


Добавление новых элементов

Метод appended

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

Packery предоставляет метод appended, который корректно интегрирует новые элементы в существующий макет.

var newItems = document.createElement('div');
newItems.classList.add('grid-item');

grid.appendChild(newItems);

pckry.appended(newItems);

Алгоритм работы метода:

  1. Элемент добавляется в DOM.
  2. Packery регистрирует его как управляемый элемент.
  3. Выполняется перерасчёт расположения.
  4. Элемент занимает ближайшую доступную позицию.

Добавление нескольких элементов выполняется через массив или NodeList:

var items = [];

for (var i = 0; i < 5; i++) {
  var item = document.createElement('div');
  item.className = 'grid-item';
  grid.appendChild(item);
  items.push(item);
}

pckry.appended(items);

Это значительно эффективнее последовательного добавления элементов.


Удаление элементов

Удаление элементов также должно происходить через API Packery, иначе внутреннее состояние сетки нарушается.

Используется метод remove.

var item = grid.querySelector('.grid-item');

pckry.remove(item);
pckry.layout();

Процесс удаления включает:

  1. Удаление элемента из внутреннего списка Packery.
  2. Удаление DOM-узла.
  3. Перестройку сетки.

Для удаления нескольких элементов:

var items = grid.querySelectorAll('.grid-item.to-delete');

pckry.remove(items);
pckry.layout();

После удаления Packery автоматически перераспределяет оставшиеся элементы.


Перерасчёт макета

При динамических изменениях размеров или структуры необходимо вручную инициировать перерасчёт макета.

Метод layout запускает алгоритм перепозиционирования.

pckry.layout();

Перерасчёт требуется в следующих ситуациях:

  • изменение размеров элементов;
  • изменение размеров контейнера;
  • добавление или удаление блоков;
  • завершение загрузки изображений;
  • изменение CSS-свойств, влияющих на размер.

Добавление элементов с анимацией

Packery поддерживает плавное появление элементов. Для этого используется CSS-анимация и правильная последовательность действий.

var item = document.createElement('div');
item.className = 'grid-item hidden';

grid.appendChild(item);

pckry.appended(item);

setTimeout(function() {
  item.classList.remove('hidden');
}, 50);

CSS:

.grid-item {
  transition: transform 0.4s, opacity 0.4s;
}

.hidden {
  opacity: 0;
}

Такой подход позволяет добиться мягкой интеграции новых элементов в сетку.


Работа с загрузкой изображений

Одной из наиболее распространённых проблем динамических сеток является загрузка изображений. Пока изображение не загрузилось, браузер не знает окончательного размера элемента.

Это может привести к неправильному расположению элементов.

Для решения используется библиотека imagesLoaded.

imagesLoaded(grid, function() {
  pckry.layout();
});

При добавлении новых элементов:

var items = document.querySelectorAll('.grid-item');

imagesLoaded(items, function() {
  pckry.appended(items);
});

Таким образом Packery выполняет перерасчёт только после получения точных размеров.


Обновление размеров элементов

Если размеры элемента изменяются динамически (например, раскрытие карточки), необходимо уведомить Packery.

Используется метод reloadItems или layout.

pckry.layout();

Если изменения затрагивают структуру элементов:

pckry.reloadItems();
pckry.layout();

reloadItems пересканирует контейнер и заново зарегистрирует элементы.


Добавление элементов через AJAX

Часто элементы подгружаются с сервера. Алгоритм работы с Packery следующий:

  1. получить HTML-разметку;
  2. создать DOM-узлы;
  3. добавить их в контейнер;
  4. уведомить Packery.

Пример:

fetch('/api/items')
  .then(response => response.text())
  .then(html => {
    var temp = document.createElement('div');
    temp.innerHTML = html;

    var items = temp.querySelectorAll('.grid-item');

    items.forEach(function(item){
      grid.appendChild(item);
    });

    pckry.appended(items);
  });

Такой подход используется при реализации infinite scroll.


Реализация бесконечной прокрутки

Infinite Scroll позволяет подгружать новые элементы по мере прокрутки страницы.

Основная логика:

window.addEventListener('scroll', function(){

  if (window.innerHeight + window.scrollY >= document.body.offsetHeight - 500) {
    loadMore();
  }

});

Функция загрузки:

function loadMore(){

  fetch('/items')
    .then(res => res.text())
    .then(html => {

      var temp = document.createElement('div');
      temp.innerHTML = html;

      var items = temp.querySelectorAll('.grid-item');

      items.forEach(function(item){
        grid.appendChild(item);
      });

      pckry.appended(items);

    });

}

После добавления новых карточек Packery автоматически перераспределяет пространство.


Перестройка сетки при изменении размеров окна

Адаптивные интерфейсы требуют пересчёта сетки при изменении размеров окна.

window.addEventListener('resize', function() {
  pckry.layout();
});

Однако частые вызовы могут снижать производительность. Используется debounce.

function debounce(fn, delay) {
  let timer;

  return function() {
    clearTimeout(timer);
    timer = setTimeout(fn, delay);
  };
}

window.addEventListener('resize', debounce(function(){
  pckry.layout();
}, 200));

Это уменьшает количество перерасчётов.


Синхронизация с Drag-and-Drop

Packery часто используется вместе с Draggabilly для перетаскивания элементов.

После перемещения элементов сетка должна обновляться.

var draggie = new Draggabilly(item);

pckry.bindDraggabillyEvents(draggie);

При добавлении новых элементов необходимо привязывать drag-обработчики:

items.forEach(function(item) {

  var draggie = new Draggabilly(item);

  pckry.bindDraggabillyEvents(draggie);

});

Это обеспечивает корректную работу динамически добавленных карточек.


Оптимизация производительности

При работе с большим количеством динамических элементов следует учитывать несколько факторов.

Пакетная обработка

Добавление элементов группами:

pckry.appended(items);

вместо последовательных вызовов.


Минимизация перерасчётов

Необходимо избегать частых вызовов layout.

Лучше объединять изменения:

pckry.reloadItems();
pckry.layout();

Использование requestAnimationFrame

Для плавного обновления интерфейса:

requestAnimationFrame(function(){
  pckry.layout();
});

Полный пример динамической сетки

HTML:

<div class="grid">
  <div class="grid-item"></div>
  <div class="grid-item"></div>
</div>

<button id="add">Добавить</button>

Jav * aScript:

var grid = document.querySelector('.grid');

var pckry = new Packery(grid, {
  itemSelector: '.grid-item',
  gutter: 10
});

document.getElementById('add').addEventListener('click', function(){

  var item = document.createElement('div');
  item.className = 'grid-item';

  grid.appendChild(item);

  pckry.appended(item);

});

CSS:

.grid {
  position: relative;
}

.grid-item {
  width: 200px;
  height: 150px;
  background: #ccc;
  margin-bottom: 10px;
}

Сетка автоматически перестраивается при добавлении новых элементов, обеспечивая плотную упаковку и эффективное использование пространства.