Dragula — это легковесная библиотека JavaScript для реализации функционала drag-and-drop без сложных зависимостей. Её основной принцип работы заключается в манипуляции DOM-элементами при перемещении внутри одного контейнера или между несколькими контейнерами. Библиотека обеспечивает плавную анимацию, удобное управление событиями и минимальные настройки для интеграции.
В Dragula все взаимодействия строятся вокруг контейнеров и элементов, которые в них находятся. Контейнер — это HTML-элемент, внутри которого находятся элементы, доступные для перетаскивания. Элемент — это объект, который можно перемещать между контейнерами или внутри одного контейнера.
const container = document.querySelector('#container');
dragula([container]);
В этом примере создаётся базовая зона перетаскивания. Элементы внутри контейнера становятся интерактивными без дополнительной конфигурации.
Когда необходимо реализовать вложенные списки или иерархические структуры, Dragula позволяет управлять как родительскими, так и дочерними контейнерами. Основной подход заключается в инициализации Dragula с массивом контейнеров, включая все уровни вложенности:
const parentContainer = document.querySelector('#parent');
const childContainers = parentContainer.querySelectorAll('.child');
dragula([parentContainer, ...childContainers], {
moves: function (el, source, handle, sibling) {
return !el.classList.contains('locked'); // предотвращение перемещения заблокированных элементов
}
});
Ключевые моменты:
moves позволяет ограничивать возможность перетаскивания
элементов в зависимости от класса или состояния.data-* для идентификации уровня вложенности и контроля
допустимых перемещений.Dragula предоставляет несколько функций для контроля логики drag-and-drop:
accepts — определяет, может ли элемент
быть помещён в конкретный контейнер.invalid — запрещает перемещение
определённых элементов.copy — позволяет создавать копии
элементов вместо их перемещения.Пример настройки для вложенных списков:
dragula([parentContainer, ...childContainers], {
moves: (el) => !el.classList.contains('locked'),
accepts: (el, target, source, sibling) => {
const sourceLevel = source.dataset.level;
const targetLevel = target.dataset.level;
return parseInt(targetLevel) <= parseInt(sourceLevel) + 1;
},
copy: false
});
Здесь используется проверка dataset.level, чтобы
элементы не могли быть перемещены на уровень выше, чем разрешено. Такой
подход позволяет строить строгую иерархию.
Dragula генерирует несколько событий, которые критически важны для работы с вложенными структурами:
drag — событие начала
перетаскивания.dragend — завершение
перетаскивания.drop — элемент успешно перемещён в
контейнер.over / out — элемент
находится над контейнером или покинул его.Пример использования событий для обновления данных и контроля вложенности:
drake.on('drop', (el, target, source, sibling) => {
const parentId = target.dataset.id;
const elementId = el.dataset.id;
console.log(`Элемент ${elementId} перемещён в контейнер ${parentId}`);
updateHierarchy(el, target);
});
Функция updateHierarchy может изменять внутреннюю
структуру данных, синхронизируя DOM с моделью приложения.
Для удобства работы с вложенными структурами часто применяются стили для hover, dragging и placeholder:
.gu-mirror {
opacity: 0.8;
border: 2px dashed #ccc;
}
.gu-transit {
opacity: 0.5;
}
.container.over {
background-color: #f0f0f0;
}
.gu-mirror — отображение элемента во время
перетаскивания..gu-transit — элемент, который временно покидает
исходный контейнер..over — визуальная подсветка контейнера, когда элемент
над ним.При глубокой вложенности и большом количестве элементов рекомендуется:
data-* для упрощённого контроля правил перемещения.moves и accepts, чтобы снизить нагрузку
на обработку событий.Для динамических списков необходимо пересоздавать массив контейнеров при добавлении новых элементов:
function refreshContainers() {
const allContainers = document.querySelectorAll('.container');
drake.containers = Array.from(allContainers);
}
document.querySelector('#add-item').addEventListener('click', () => {
const newItem = document.createElement('div');
newItem.classList.add('item');
document.querySelector('#parent').appendChild(newItem);
refreshContainers();
});
Такой подход гарантирует, что новые элементы автоматически участвуют в drag-and-drop без полной переинициализации Dragula.
Dragula не ограничивает структуру данных в приложении. Часто используется синхронизация DOM с JSON-моделью:
function serialize(container) {
const children = Array.from(container.children);
return children.map(child => ({
id: child.dataset.id,
children: serialize(child.querySelector('.children') || document.createElement('div'))
}));
}
Это позволяет поддерживать вложенные списки в виде дерева и легко интегрировать с серверной частью или локальным хранилищем.
Эти подходы обеспечивают эффективную работу Dragula с вложенными структурами, управляют логикой перемещения и поддерживают синхронизацию с моделью данных, сохраняя производительность и удобство визуальной обработки элементов.