Dragula — это легковесная библиотека для реализации drag-and-drop интерфейсов в браузере. Она позволяет перемещать элементы между контейнерами и внутри них без необходимости писать сложные обработчики событий. В основе работы Dragula лежит концепция контейнеров и элементов, которые можно перетаскивать. Контейнеры — это DOM-узлы, внутри которых располагаются перетаскиваемые элементы, а элементы — это дочерние узлы контейнера, которые пользователи могут перемещать.
Для начала работы создаётся объект Dragula, которому передаётся массив контейнеров:
const containers = [document.getElementById('left'), document.getElementById('right')];
const drake = dragula(containers);
Библиотека автоматически обрабатывает события mousedown,
mousemove и mouseup для элементов внутри
контейнеров, создавая визуальный эффект перетаскивания.
Основные опции Dragula:
moves — функция, определяющая, можно ли перемещать
конкретный элемент.accepts — функция, определяющая, можно ли помещать
элемент в конкретный контейнер.copy — позволяет создавать копии элементов при
перемещении.revertOnSpill — возвращает элемент в исходное место,
если он был “выброшен” вне контейнера.removeOnSpill — удаляет элемент при “выбросе” вне
контейнера.Пример использования moves и accepts:
const drake = dragula(containers, {
moves: (el, source, handle, sibling) => handle.classList.contains('handle'),
accepts: (el, target, source, sibling) => !target.classList.contains('locked')
});
Кэширование состояния — критически важный аспект для сложных интерфейсов с множественными контейнерами и большим количеством элементов. Dragula не предоставляет встроенного механизма хранения состояния, поэтому его необходимо реализовывать вручную. Основная цель кэширования — сохранять текущее расположение элементов и восстанавливать его при перезагрузке страницы или динамическом изменении интерфейса.
Наиболее удобные форматы:
localStorage или
sessionStorage.Пример структуры состояния:
const state = {
left: ['item1', 'item3'],
right: ['item2', 'item4']
};
Где ключи — идентификаторы контейнеров, а массивы — идентификаторы элементов в порядке отображения.
Dragula генерирует ряд событий, которые можно использовать для кэширования:
drag — начинается перемещение элемента.drop — элемент помещён в новый контейнер.remove — элемент удалён при
removeOnSpill.Пример обновления состояния при событии drop:
drake.on('drop', (el, target, source, sibling) => {
const sourceId = source.id;
const targetId = target.id;
const elementId = el.id;
// Удаляем из старого контейнера
state[sourceId] = state[sourceId].filter(id => id !== elementId);
// Вставляем в новый контейнер в нужное место
const index = Array.from(target.children).indexOf(el);
state[targetId].splice(index, 0, elementId);
// Сохраняем состояние в localStorage
localStorage.setItem('dragulaState', JSON.stringify(state));
});
Для восстановления состояния после перезагрузки страницы необходимо:
localStorage.Пример восстановления:
const savedState = JSON.parse(localStorage.getItem('dragulaState') || '{}');
for (const containerId in savedState) {
const container = document.getElementById(containerId);
savedState[containerId].forEach(elId => {
const el = document.getElementById(elId);
if (el) container.appendChild(el);
});
}
localStorage можно использовать функцию
дебаунса, чтобы состояние сохранялось не при каждом перемещении, а через
фиксированный интервал.Dragula легко интегрируется с React, Vue и Angular, если хранить состояние в соответствующих хранилищах (например, Redux или Vuex). События Dragula можно связывать с действиями этих библиотек для автоматического обновления виртуального состояния.
Пример с React:
drake.on('drop', (el, target, source, sibling) => {
dispatch({
type: 'UPDATE_DRAG_STATE',
payload: { elementId: el.id, from: source.id, to: target.id, index: Array.from(target.children).indexOf(el) }
});
});
Такой подход обеспечивает синхронизацию DOM и состояния приложения без необходимости перезаписывать всё вручную.
Кэширование состояния в Dragula требует аккуратного подхода к
структуре данных и событиям библиотеки. Основная стратегия — отслеживать
drop и remove, сохранять структуру контейнеров
и порядок элементов, использовать localStorage или
аналогичные механизмы для персистентности и при необходимости
интегрировать с архитектурой приложения для динамических обновлений
интерфейса.