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

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

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


Базовая инициализация сетки

Перед добавлением элементов необходимо создать контейнер и инициализировать Packery.

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

Инициализация:

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

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

Параметры:

  • itemSelector — селектор элементов сетки
  • gutter — расстояние между элементами

После инициализации Packery рассчитывает позиции всех элементов.


Общая схема добавления элементов

Добавление элементов в Packery выполняется в три этапа:

  1. Создание DOM-элемента
  2. Добавление его в контейнер
  3. Сообщение Packery о новом элементе

Последний шаг обязателен, иначе библиотека не узнает о появлении нового блока.


Метод appended()

Основной способ добавления элементов — использование метода appended().

Этот метод:

  • регистрирует новый элемент в Packery
  • рассчитывает его позицию
  • перестраивает сетку

Пример добавления элемента

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

grid.appendChild(newItem);

pckry.appended(newItem);

После выполнения Packery:

  1. добавляет элемент в систему управления
  2. вычисляет его положение
  3. перестраивает расположение других элементов при необходимости

Добавление нескольких элементов

Packery поддерживает добавление массивов элементов.

var items = [];

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

pckry.appended(items);

Преимущества добавления сразу нескольких элементов:

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

Добавление элементов из HTML-шаблона

Часто новые элементы создаются не через createElement, а из HTML-шаблонов.

var html = `
  <div class="grid-item">
    <img src="image.jpg">
  </div>
`;

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

var newItem = temp.firstElementChild;

grid.appendChild(newItem);

pckry.appended(newItem);

Такой подход используется в:

  • карточках товаров
  • галереях
  • лентах новостей
  • интерфейсах drag-and-drop

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

При добавлении большого количества элементов рекомендуется использовать DocumentFragment.

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

var fragment = document.createDocumentFragment();
var items = [];

for (var i = 0; i < 10; i++) {
  var item = document.createElement('div');
  item.className = 'grid-item';

  fragment.appendChild(item);
  items.push(item);
}

grid.appendChild(fragment);

pckry.appended(items);

Преимущества:

  • высокая производительность
  • минимизация операций DOM
  • оптимальная работа при больших сетках

Перестроение сетки после добавления

В большинстве случаев appended() автоматически выполняет перерасчёт расположения элементов.

Однако в сложных сценариях может потребоваться явный вызов:

pckry.layout();

Это необходимо если:

  • изменились размеры элементов
  • загрузились изображения
  • изменились CSS-классы

Добавление элементов после загрузки изображений

Проблема: если изображения ещё не загрузились, Packery может неправильно рассчитать размеры элементов.

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

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

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

grid.appendChild(newItem);
pckry.appended(newItem);

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

Это гарантирует:

  • корректный расчёт размеров
  • отсутствие наложения элементов
  • стабильную сетку

Анимация появления элементов

Packery поддерживает плавное появление элементов благодаря CSS-transition.

Пример:

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

При добавлении можно установить начальную прозрачность:

newItem.style.opacity = 0;

grid.appendChild(newItem);
pckry.appended(newItem);

setTimeout(function(){
  newItem.style.opacity = 1;
}, 50);

Результат:

  • плавное появление элемента
  • мягкое перестроение сетки

Использование jQuery-версии

Packery также поддерживает работу через jQuery.

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

var $item = $('<div class="grid-item"></div>');

$('.grid').append($item)
  .packery('appended', $item);

Добавление нескольких элементов

var $items = $([
  '<div class="grid-item"></div>',
  '<div class="grid-item"></div>'
].join(''));

$('.grid').append($items)
  .packery('appended', $items);

Этот подход часто используется вместе с:

  • AJAX-подгрузкой
  • бесконечной прокруткой
  • динамическими фильтрами

Добавление элементов при бесконечной прокрутке

Типичный сценарий — Infinite Scroll.

После загрузки новых данных:

fetch('/api/items')
  .then(res => res.json())
  .then(data => {

    var items = [];

    data.forEach(function(itemData){

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

      item.innerHTML = `
        <img src="${itemData.image}">
      `;

      grid.appendChild(item);
      items.push(item);

    });

    pckry.appended(items);

  });

Packery автоматически:

  • размещает новые элементы
  • перестраивает сетку
  • сохраняет плотное заполнение пространства

Добавление элементов с учётом размеров

Если элементы имеют разные размеры, необходимо задать CSS-классы.

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

CSS:

.grid-item {
  width: 100px;
  height: 100px;
}

.grid-item--width2 {
  width: 210px;
}

.grid-item--height2 {
  height: 210px;
}

При добавлении:

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

grid.appendChild(item);

pckry.appended(item);

Packery автоматически учитывает размеры и размещает элемент оптимальным образом.


Обновление размеров после добавления

Если размер элемента изменился после добавления (например, из-за динамического контента), используется метод:

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

Это приводит к:

  1. повторному сканированию элементов
  2. перерасчёту размеров
  3. обновлению позиций

Типичные ошибки при добавлении элементов

Элемент добавлен в DOM, но Packery не знает о нём

grid.appendChild(item);

Без вызова:

pckry.appended(item);

В результате элемент:

  • появляется в DOM
  • но не участвует в сетке

Неверный селектор itemSelector

Если класс нового элемента не совпадает:

itemSelector: '.grid-item'

а элемент имеет:

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

Packery проигнорирует элемент.


Неправильный расчёт размеров

Проблема возникает если:

  • изображения не загрузились
  • размеры задаются через JS
  • используются веб-шрифты

Решение:

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

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

При интенсивной подгрузке контента рекомендуется:

  1. добавлять элементы батчами
  2. использовать DocumentFragment
  3. вызывать layout() реже

Пример:

var batch = [];

for (var i = 0; i < 20; i++) {

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

  grid.appendChild(item);
  batch.push(item);

}

pckry.appended(batch);

Такой подход значительно уменьшает нагрузку на браузер.


Добавление элементов вместе с drag-and-drop

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

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

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

grid.appendChild(item);

pckry.appended(item);

var draggie = new Draggabilly(item);

pckry.bindDraggabillyEvents(draggie);

После этого элемент:

  • добавлен в сетку
  • может перетаскиваться
  • участвует в перерасчёте layout

Добавление элементов через события интерфейса

Пример кнопки:

<button id="add">Добавить элемент</button>
document.getElementById('add').addEventListener('click', function(){

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

  grid.appendChild(item);

  pckry.appended(item);

});

После нажатия кнопки:

  • элемент появляется в контейнере
  • Packery пересчитывает сетку
  • блок занимает ближайшее свободное место

Внутренний механизм размещения новых элементов

После вызова appended() Packery:

  1. добавляет элемент в список items
  2. вычисляет его размер
  3. находит свободное пространство
  4. размещает элемент
  5. обновляет координаты сетки

Алгоритм старается:

  • минимизировать пустоты
  • уплотнить размещение
  • сохранить визуальную стабильность интерфейса

Благодаря этому Packery подходит для интерфейсов, где элементы постоянно добавляются и удаляются.