Dragula — это лёгкая библиотека для реализации drag-and-drop функционала на веб-страницах. Она минимизирует необходимость писать сложные обработчики событий мыши и обеспечивает плавную перетаскиваемость элементов между контейнерами. Для подключения Dragula используется пакет через npm или подключение через CDN:
<link rel="stylesheet" href="https://unpkg.com/dragula/dist/dragula.min.css">
<script src="https://unpkg.com/dragula/dist/dragula.min.js"></script>
Инициализация производится вызовом функции
dragula(containers, options):
const containers = [document.getElementById('left'), document.getElementById('right')];
const drake = dragula(containers, {
moves: (el, source, handle, sibling) => handle.classList.contains('handle'),
revertOnSpill: true
});
Здесь moves задаёт правило, по которому
элемент можно перетаскивать, а
revertOnSpill возвращает элемент в
исходное место при отмене дропа.
Dragula предоставляет набор событий, которые позволяют точно контролировать процесс перетаскивания:
drag — событие начала перетаскивания
элемента.dragend — завершение
перетаскивания.drop — успешная установка элемента в
новый контейнер.cancel — отмена перетаскивания.remove — удаление элемента из DOM при
переносе вне контейнеров.Пример использования событий:
drake.on('drop', (el, target, source, sibling) => {
console.log('Элемент перенесён:', el, 'в контейнер', target);
});
drake.on('drag', (el) => {
el.style.opacity = '0.5';
});
drake.on('dragend', (el) => {
el.style.opacity = '';
});
События позволяют управлять визуальными эффектами без необходимости напрямую вмешиваться в DOM-структуру, что снижает нагрузку на reflow и repaint.
Reflow и repaint — это процессы браузера, которые происходят при изменении DOM или стилей, влияя на производительность страницы. Dragula изначально оптимизирована для минимизации этих операций за счёт следующих механизмов:
Во время перетаскивания Dragula создаёт клон перетаскиваемого элемента, который визуально повторяет исходный, но не изменяет сразу DOM контейнера. Это снижает количество пересчётов layout.
drake.on('drag', (el) => {
const clone = el.cloneNode(true);
clone.classList.add('dragging-clone');
document.body.appendChild(clone);
});
Использование клона позволяет браузеру обрабатывать анимацию без полной перестройки страницы.
Изменения стилей элементов выполняются через inline стили или классы CSS, что позволяет браузеру объединять несколько изменений в один reflow:
.dragging {
transform: translate(0, 0);
transition: transform 0.2s ease;
}
Применение transform вместо прямой манипуляции
top/left предотвращает повторные пересчёты layout, так как
transform не влияет на поток документа.
Dragula отслеживает только те контейнеры, которые указаны при инициализации. Это снижает вычислительную нагрузку при проверке позиции перетаскиваемого элемента.
const drake = dragula([document.getElementById('list1'), document.getElementById('list2')]);
Добавление лишних контейнеров напрямую увеличивает количество проверок и потенциальные reflows.
Иногда после drop нужно обновить сложный DOM (например, таблицы или списки с большим количеством элементов). Для уменьшения эффекта reflow эти изменения можно выполнять через requestAnimationFrame:
drake.on('drop', (el, target) => {
requestAnimationFrame(() => {
target.appendChild(el);
});
});
Это позволяет браузеру завершить текущие рендер-процессы до внесения изменений, обеспечивая плавность анимации.
Dragula активно использует классы для визуальных эффектов. Например, для отображения placeholder:
.drake-placeholder {
border: 2px dashed #aaa;
height: 50px;
margin: 5px 0;
}
Добавление или удаление класса вызывает repaint, но не приводит к перерасчёту layout всего документа.
moves и accepts —
фильтруют элементы, которые реально можно перетаскивать, что снижает
лишние события.copy — позволяет работать с клонами
элементов без изменения исходного DOM.revertOnSpill — предотвращает частые
перестройки DOM, если элемент случайно отпущен вне контейнера.invalid — исключает ненужные элементы
из drag-and-drop логики, что экономит вычисления.Пример продвинутой конфигурации:
const drake = dragula(containers, {
moves: (el, source, handle) => handle.classList.contains('handle'),
accepts: (el, target) => !target.classList.contains('disabled'),
copy: true,
revertOnSpill: true,
invalid: el => el.tagName === 'INPUT'
});
Эта настройка минимизирует лишние события, снижает нагрузку на рендер и поддерживает плавное взаимодействие даже при больших списках элементов.
Dragula сочетает простоту использования с эффективной оптимизацией браузерных операций, позволяя реализовывать сложный drag-and-drop функционал без заметного влияния на производительность страниц. Правильная работа с клонами, классами CSS и ограничение количества операций reflow/repaint обеспечивает стабильную работу интерфейсов с большим количеством элементов.