Shopify Draggable — это мощная библиотека для реализации перетаскивания элементов в интерфейсе. Одним из ключевых аспектов работы с ней является правильная синхронизация визуальных изменений в DOM с внутренними данными приложения. Без надёжной синхронизации появляется риск рассогласования состояния интерфейса и модели данных, что может привести к багам и некорректной работе логики.
Для эффективной синхронизации важно, чтобы каждый перетаскиваемый
элемент имел уникальный идентификатор. Обычно это атрибут
data-id или id:
<ul id="sortable-list">
<li data-id="1">Элемент 1</li>
<li data-id="2">Элемент 2</li>
<li data-id="3">Элемент 3</li>
</ul>
Внутренние данные представляются массивом объектов:
let items = [
{ id: 1, name: 'Элемент 1' },
{ id: 2, name: 'Элемент 2' },
{ id: 3, name: 'Элемент 3' }
];
Такое сопоставление позволяет легко отслеживать соответствие между DOM и данными.
Инициализация Draggable требует передачи контейнера и выбора элементов для перетаскивания:
const container = document.getElementById('sortable-list');
const draggable = new Draggable.Sortable(container, {
draggable: 'li',
mirror: {
constrainDimensions: true
}
});
Параметр mirror.constrainDimensions обеспечивает
корректное отображение “зеркала” перетаскиваемого элемента с сохранением
размеров.
Основные события, которые используются для синхронизации:
sortable:stop — вызывается после завершения
перетаскивания.sortable:sort — вызывается при каждом изменении позиции
во время перетаскивания.sortable:swappable:swap — используется при обмене
местами элементов.Пример синхронизации данных после завершения перетаскивания:
draggable.on('sortable:stop', (event) => {
const newOrder = Array.from(container.children).map(el => parseInt(el.dataset.id));
items.sort((a, b) => {
return newOrder.indexOf(a.id) - newOrder.indexOf(b.id);
});
console.log('Новый порядок данных:', items);
});
Здесь newOrder представляет порядок элементов в DOM, а
метод sort переставляет массив объектов items
в соответствии с текущим визуальным порядком.
Для длинных списков важно минимизировать количество операций с DOM. Рекомендуется:
sortable:stop), а не на каждом шаге.Если элементы добавляются или удаляются динамически, необходимо пересоздавать экземпляр Draggable или обновлять контейнер:
function refreshDraggable() {
draggable.destroy();
draggable = new Draggable.Sortable(container, {
draggable: 'li',
mirror: { constrainDimensions: true }
});
}
Это гарантирует, что новые элементы корректно участвуют в перетаскивании и синхронизации данных.
Draggable поддерживает вложенные структуры и возможность работы с несколькими контейнерами. Для синхронизации данных в этом случае удобно хранить структуру в виде дерева:
let tree = [
{ id: 1, name: 'Группа 1', children: [{ id: 2, name: 'Элемент 2' }] },
{ id: 3, name: 'Элемент 3', children: [] }
];
При изменении порядка или перемещении элементов между группами
необходимо обновлять соответствующие массивы children и
верхний уровень. Используются события sortable:stop с
проверкой event.oldContainer и
event.newContainer для определения перемещения между
контейнерами.
В случае загрузки данных с сервера порядок элементов в DOM должен синхронизироваться с серверной моделью. После перетаскивания вызывается API для обновления порядка:
draggable.on('sortable:stop', async (event) => {
const order = Array.from(container.children).map(el => el.dataset.id);
await fetch('/api/update-order', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ order })
});
});
Асинхронная синхронизация позволяет поддерживать согласованность состояния интерфейса и серверных данных.
Для надёжной работы важно учитывать ошибки:
try {
await syncWithServer(newOrder);
} catch (e) {
alert('Ошибка синхронизации. Порядок элементов восстановлен.');
renderList(items); // Возврат к предыдущему состоянию
}
data-*
атрибутах.Эти практики помогают избежать непредсказуемого поведения и делают синхронизацию более прозрачной и поддерживаемой.