Dragula — это легковесная библиотека для реализации перетаскивания элементов (drag-and-drop) на веб-страницах. Она позволяет легко управлять порядком элементов в списках и таблицах без использования сложных фреймворков. Основной принцип работы Dragula заключается в отслеживании перетаскивания DOM-элементов и автоматическом обновлении их положения в документе.
Для работы с таблицами необходимо подключить библиотеку:
<link rel="stylesheet" href="https://unpkg.com/dragula/dist/dragula.min.css">
<script src="https://unpkg.com/dragula/dist/dragula.min.js"></script>
Инициализация Dragula осуществляется через вызов функции
dragula() с массивом контейнеров, элементы которых будут
перетаскиваться:
const tableBody = document.querySelector('tbody');
const drake = dragula([tableBody]);
Ключевые моменты:
dragula() принимает массив контейнеров DOM-элементов. В
случае таблицы это чаще всего <tbody> или набор
<tr> внутри <tbody>.Для того чтобы перетаскивание работало корректно, важно учитывать
структуру <tr> и <td>:
dragula([tableBody], {
moves: function (el, source, handle, sibling) {
return el.tagName === 'TR';
}
});
Пояснения:
moves — функция, определяющая, какие элементы можно
перетаскивать. В данном случае разрешается только перетаскивание строк
<tr>.
Параметры функции:
el — элемент, который пользователь пытается
перетащить.source — родительский контейнер элемента.handle — элемент, за который пользователь держится при
перетаскивании.sibling — следующий элемент перед текущей позицией
вставки.Dragula позволяет задавать контейнеры, между которыми элементы могут перемещаться, и запрещать перетаскивание в другие области:
const drake = dragula([tableBody], {
accepts: function(el, target, source, sibling) {
return target.tagName === 'TBODY';
}
});
Особенности:
accepts определяет, в какие контейнеры элемент может
быть помещён.true или false
определяет возможность вставки.Dragula использует встроенные CSS-классы для управления визуальной составляющей перетаскивания:
.gu-mirror — отображаемая копия перетаскиваемого
элемента..gu-hide — скрывает элемент в исходном контейнере при
перетаскивании..gu-unselectable — предотвращает выделение текста при
перетаскивании.Можно добавлять свои классы для изменения внешнего вида:
drake.on('drag', function(el) {
el.classList.add('dragging');
});
drake.on('drop', function(el, target, source, sibling) {
el.classList.remove('dragging');
});
Dragula предоставляет набор событий, которые позволяют отслеживать все стадии перетаскивания:
Пример использования:
drake.on('drop', function(el, target, source, sibling) {
console.log('Элемент перемещён:', el);
});
Чтобы после перетаскивания обновить порядок строк таблицы, удобно
использовать метод children контейнера:
drake.on('drop', function(el, target) {
const rows = Array.from(target.children);
rows.forEach((row, index) => {
row.dataset.index = index;
});
});
Пояснение:
dataset.index позволяет хранить
актуальный индекс строки.После изменения порядка строк данные можно отправлять на сервер для сохранения:
drake.on('drop', function(el, target) {
const order = Array.from(target.children).map(row => row.dataset.id);
fetch('/update-order', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ order })
});
});
Особенности:
row.dataset.id хранит уникальный идентификатор
строки.<tbody>:
элементы должны быть прямыми потомками <tbody>;
вложенные таблицы требуют отдельной инициализации.touch-action: none.Можно запретить перетаскивание первой или последней строки таблицы:
dragula([tableBody], {
moves: function(el, source, handle) {
return !el.classList.contains('fixed');
}
});
.fixed применяется к строкам, которые нельзя
перемещать.Для таблиц с большим количеством строк:
documentFragment при динамическом
обновлении DOM после перетаскивания.drag и drop только на
контейнер, чтобы не перегружать браузер.drop.const tableBody = document.querySelector('tbody');
const drake = dragula([tableBody], {
moves: (el) => el.tagName === 'TR' && !el.classList.contains('fixed'),
accepts: (el, target) => target.tagName === 'TBODY'
});
drake.on('drop', (el, target) => {
const order = Array.from(target.children).map(row => row.dataset.id);
fetch('/update-order', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ order })
});
el.classList.remove('dragging');
});
drake.on('drag', (el) => el.classList.add('dragging'));
<tr> могут быть перетаскиваемыми,
кроме тех, что имеют класс .fixed.