Библиотека 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 выполняется в три этапа:
Последний шаг обязателен, иначе библиотека не узнает о появлении нового блока.
Основной способ добавления элементов — использование метода
appended().
Этот метод:
var newItem = document.createElement('div');
newItem.className = 'grid-item';
grid.appendChild(newItem);
pckry.appended(newItem);
После выполнения Packery:
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);
Преимущества добавления сразу нескольких элементов:
Часто новые элементы создаются не через 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);
Такой подход используется в:
При добавлении большого количества элементов рекомендуется использовать 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);
Преимущества:
В большинстве случаев appended() автоматически выполняет
перерасчёт расположения элементов.
Однако в сложных сценариях может потребоваться явный вызов:
pckry.layout();
Это необходимо если:
Проблема: если изображения ещё не загрузились, 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);
Результат:
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);
Этот подход часто используется вместе с:
Типичный сценарий — 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();
Это приводит к:
grid.appendChild(item);
Без вызова:
pckry.appended(item);
В результате элемент:
Если класс нового элемента не совпадает:
itemSelector: '.grid-item'
а элемент имеет:
<div class="item"></div>
Packery проигнорирует элемент.
Проблема возникает если:
Решение:
imagesLoaded(grid, function() {
pckry.layout();
});
При интенсивной подгрузке контента рекомендуется:
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);
Такой подход значительно уменьшает нагрузку на браузер.
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);
После этого элемент:
Пример кнопки:
<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);
});
После нажатия кнопки:
После вызова appended() Packery:
Алгоритм старается:
Благодаря этому Packery подходит для интерфейсов, где элементы постоянно добавляются и удаляются.