Работа с таблицами

Dragula — это легковесная библиотека для реализации перетаскивания элементов (drag-and-drop) на веб-страницах. Она позволяет легко управлять порядком элементов в списках и таблицах без использования сложных фреймворков. Основной принцип работы Dragula заключается в отслеживании перетаскивания DOM-элементов и автоматическом обновлении их положения в документе.


Инициализация Dragula

Для работы с таблицами необходимо подключить библиотеку:

<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>.
  • Dragula автоматически создает копию перемещаемого элемента и отображает его при перетаскивании, сохраняя визуальную целостность таблицы.

Перетаскивание строк таблицы

Для того чтобы перетаскивание работало корректно, важно учитывать структуру <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 для таблиц

Dragula предоставляет набор событий, которые позволяют отслеживать все стадии перетаскивания:

  • drag — начало перетаскивания элемента.
  • drop — элемент был перемещён в новый контейнер.
  • over — элемент находится над контейнером.
  • out — элемент покинул контейнер.
  • cancel — перетаскивание отменено.
  • cloned — элемент был скопирован при перетаскивании.

Пример использования:

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 позволяет хранить актуальный индекс строки.
  • Такой подход удобен для синхронизации с бэкендом через AJAX-запросы.

Интеграция с сервером

После изменения порядка строк данные можно отправлять на сервер для сохранения:

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 хранит уникальный идентификатор строки.
  • Сервер получает новый порядок строк и сохраняет его в базе данных.

Особенности Dragula при работе с таблицами

  1. Совместимость с <tbody>: элементы должны быть прямыми потомками <tbody>; вложенные таблицы требуют отдельной инициализации.
  2. Стилизация: для корректной работы перетаскивания желательно использовать фиксированную высоту строк и отступы, чтобы избежать визуальных смещений.
  3. Мобильные устройства: Dragula поддерживает сенсорные события, но в таблицах иногда требуется дополнительная настройка CSS 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.
  • Новый порядок строк автоматически отправляется на сервер.
  • Визуальное состояние перетаскивания контролируется через CSS-классы.