Shopify Draggable — это современная библиотека для создания интерактивного перетаскивания элементов в веб-интерфейсах. Одной из ключевых задач при работе с динамическим контентом является интеграция асинхронной загрузки элементов с системой перетаскивания. Этот процесс позволяет создавать интерфейсы, где элементы подгружаются по мере необходимости без полной перезагрузки страницы, сохраняя при этом функциональность Drag & Drop.
Draggable оперирует элементами DOM, присутствующими на странице в
момент инициализации. Когда новые элементы подгружаются асинхронно
(через fetch, AJAX или другие методы), их необходимо
интегрировать в существующий Draggable-инстанс. Для этого применяются
два подхода:
Пересоздание Draggable после загрузки элементов Каждый раз после добавления нового контента создается новый объект Draggable или переинициализируется существующий.
import {Draggable} from '@shopify/draggable';
const container = document.querySelector('.list');
let draggable = new Draggable(container, {
draggable: '.item'
});
async function loadMoreItems() {
const response = await fetch('/items');
const items = await response.text();
container.insertAdjacentHTML('beforeend', items);
// Переинициализация Draggable
draggable.destroy();
draggable = new Draggable(container, {
draggable: '.item'
});
}
Этот метод прост, но может быть ресурсоемким при частых обновлениях большого количества элементов.
Использование событий и подписка на новые
элементы Более оптимальный способ — подписка на добавление
новых элементов и привязка их к существующим Draggable-инстансам через
addDraggable.
const draggable = new Draggable(document.querySelector('.list'), {
draggable: '.item'
});
async function appendItems() {
const response = await fetch('/items');
const fragment = document.createRange().createContextualFragment(await response.text());
const newItems = fragment.querySelectorAll('.item');
document.querySelector('.list').appendChild(fragment);
// Добавление новых элементов в существующий Draggable
newItems.forEach(item => draggable.addDraggable(item));
}
Этот подход обеспечивает плавную интеграцию и снижает нагрузку на систему.
Draggable предоставляет множество событий: drag:start,
drag:move, drag:stop,
sortable:sorted и другие. При работе с динамическим
содержимым важно правильно привязывать обработчики к новым элементам.
Для этого рекомендуется использовать делегирование событий или подписку
на событие Draggable после добавления новых элементов.
draggable.on('drag:start', (event) => {
console.log('Начало перетаскивания', event.source);
});
async function loadAndBind() {
const response = await fetch('/items');
const itemsHTML = await response.text();
const container = document.querySelector('.list');
container.insertAdjacentHTML('beforeend', itemsHTML);
const newItems = container.querySelectorAll('.item');
newItems.forEach(item => {
draggable.addDraggable(item);
});
// Подписка на события для новых элементов
newItems.forEach(item => {
item.addEventListener('mouseenter', () => {
item.classList.add('hovered');
});
});
}
При работе с большим количеством элементов часто используется виртуализация — рендеринг только видимых элементов списка. В этом случае Draggable необходимо интегрировать с виртуализатором.
addDraggable для вновь
появившихся элементов и removeDraggable для скрытых.Пример:
const draggable = new Draggable(document.querySelector('.list'), {
draggable: '.item'
});
// Функция обновления элементов при прокрутке
function updateVisibleItems(visibleItems) {
draggable.removeDraggable(document.querySelectorAll('.item'));
visibleItems.forEach(item => draggable.addDraggable(item));
}
При асинхронной загрузке элементов следует учитывать следующие аспекты:
DocumentFragment для вставки большого количества элементов
за одну операцию DOM.addDraggable, избегая полного уничтожения и
повторной инициализации.Draggable поддерживает вложенные контейнеры. Для динамически загружаемых элементов в nested списках важно:
Пример вложенной структуры:
const containers = document.querySelectorAll('.nested-list');
const draggable = new Draggable(containers, {
draggable: '.nested-item',
mirror: {
constrainDimensions: true
}
});
async function addNestedItem(parent) {
const response = await fetch(`/nested-items?parent=${parent.id}`);
const newItem = document.createRange().createContextualFragment(await response.text());
parent.appendChild(newItem);
draggable.addDraggable(newItem.querySelector('.nested-item'));
}
Эти методы позволяют поддерживать корректное перетаскивание элементов даже в сложных интерфейсах с асинхронной подгрузкой содержимого, обеспечивая плавность работы и высокую отзывчивость интерфейса.