При работе с большими списками элементов в Shopify Draggable критически важно учитывать производительность и отзывчивость интерфейса. Даже при нескольких сотнях элементов стандартные операции перетаскивания могут вызвать заметные задержки, если подход реализован неправильно. Основные принципы эффективной работы с большими списками:
drag:start, drag:move, drag:stop
должны выполнять минимум вычислений.Простейшая инициализация Draggable для списка с сотнями элементов:
import { Draggable } from '@shopify/draggable';
const container = document.querySelector('.list-container');
const draggable = new Draggable(container, {
draggable: '.list-item',
delay: 0,
mirror: {
constrainDimensions: true
}
});
draggable.on('drag:start', () => {
container.classList.add('dragging-active');
});
draggable.on('drag:stop', () => {
container.classList.remove('dragging-active');
});
Ключевые моменты:
draggable указывает на селектор элементов, которые
можно перетаскивать.mirror.constrainDimensions помогает предотвратить
визуальные скачки зеркального элемента при больших списках.drag:start и drag:stop должны
ограничиваться только необходимыми действиями для минимизации
нагрузки.При работе с тысячами элементов важно не рендерить их все одновременно. Подход с виртуализацией реализуется через динамическое создание и удаление DOM-элементов в пределах видимой области:
function renderVisibleItems(items, scrollTop, containerHeight, itemHeight) {
const startIndex = Math.floor(scrollTop / itemHeight);
const endIndex = Math.min(items.length, Math.ceil((scrollTop + containerHeight) / itemHeight));
const fragment = document.createDocumentFragment();
for (let i = startIndex; i < endIndex; i++) {
const item = document.createElement('div');
item.className = 'list-item';
item.textContent = items[i].name;
fragment.appendChild(item);
}
const container = document.querySelector('.list-container');
container.innerHTML = '';
container.appendChild(fragment);
}
Советы по интеграции с Draggable:
draggable.destroy() и повторной инициализации.Draggable поддерживает делегирование, что особенно важно для длинных списков:
const draggable = new Draggable(document.body, {
draggable: '.list-item',
handle: '.drag-handle',
delay: 100,
mirror: {
constrainDimensions: true
}
});
Преимущества делегирования:
Для улучшения UX при больших списках стоит ограничивать область, в пределах которой можно перемещать элементы:
const draggable = new Draggable(container, {
draggable: '.list-item',
mirror: {
constrainDimensions: true,
appendTo: container
}
});
Это предотвращает «выскальзывание» элементов за пределы контейнера и снижает количество лишних перерисовок браузера.
Для больших списков крайне важно отслеживать изменения порядка элементов без постоянного обращения к DOM:
draggable.on('drag:stop', (event) => {
const newIndex = Array.from(container.children).indexOf(event.source);
const movedItem = items.splice(event.oldIndex, 1)[0];
items.splice(newIndex, 0, movedItem);
});
event.oldIndex и event.source позволяют
обновлять массив данных напрямую, не вызывая полное
перерендеривание.Для списков более 500–1000 элементов рекомендуется:
requestAnimationFrame для сложных
вычислений при событии drag:move.drag:over или drag:out, если они не
используются.При подгрузке данных через API или добавлении элементов на лету важно:
draggable.destroy() +
new Draggable(...) или использовать делегирование.const items = Array.from({ length: 1000 }, (_, i) => ({ id: i, name: `Item ${i}` }));
const container = document.querySelector('.list-container');
function render() {
const fragment = document.createDocumentFragment();
items.slice(0, 50).forEach(item => {
const div = document.createElement('div');
div.className = 'list-item';
div.textContent = item.name;
fragment.appendChild(div);
});
container.innerHTML = '';
container.appendChild(fragment);
}
render();
const draggable = new Draggable(container, {
draggable: '.list-item',
mirror: { constrainDimensions: true },
delay: 50
});
draggable.on('drag:stop', (evt) => {
const oldIndex = Array.from(container.children).indexOf(evt.source);
const moved = items.splice(oldIndex, 1)[0];
const newIndex = Math.min(oldIndex, items.length);
items.splice(newIndex, 0, moved);
});
Такой подход сочетает виртуализацию, делегирование и минимизацию операций с DOM, что обеспечивает плавную работу Draggable даже на очень больших списках.