Сортируемые списки с группировкой

Dragula — это библиотека для организации перетаскивания элементов между контейнерами и внутри одного контейнера. Она отличается минималистичным API, простотой интеграции и возможностью работать как с одиночными списками, так и с группами контейнеров. Основная цель — сделать взаимодействие с DOM интуитивным, не требуя сложной настройки событий.

Контейнеры и элементы Контейнер — это любой элемент DOM, который будет содержать перетаскиваемые элементы. Элемент — это отдельный объект внутри контейнера, который можно перетаскивать. Dragula автоматически обрабатывает перетаскивание элементов, изменяя их позицию в DOM.

const containers = [document.getElementById('list1'), document.getElementById('list2')];
const drake = dragula(containers);

В данном примере создаются два контейнера, между которыми можно перемещать элементы. Dragula отслеживает события drag, drop, over, out и cancel, позволяя гибко реагировать на действия пользователя.

Настройка поведения

Ограничение на перетаскивание Для ограничения перетаскиваемых элементов используется функция moves:

const drake = dragula(containers, {
  moves: function (el, source, handle, sibling) {
    return handle.classList.contains('handle'); // перетаскивать только по специальной ручке
  }
});

Контроль над сбросом элементов Функция accepts позволяет определить, можно ли перемещать элемент в конкретный контейнер:

drake = dragula(containers, {
  accepts: function (el, target, source, sibling) {
    return target.id !== 'list2'; // запрещаем перемещать в list2
  }
});

Копирование элементов Dragula поддерживает копирование вместо перемещения через опцию copy:

drake = dragula(containers, {
  copy: true
});

Это позволяет использовать один контейнер как источник шаблонов, не изменяя его содержимое при переносе элементов.

События Dragula

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

  • drag: начинается перетаскивание элемента
  • drop: элемент сброшен в контейнер
  • over: элемент наведен на контейнер
  • out: элемент покинул контейнер
  • cancel: перетаскивание отменено
  • cloned: создаётся копия элемента (при copy: true)

Пример подписки на события:

drake.on('drop', function (el, target, source, sibling) {
  console.log(`Элемент ${el.textContent} перемещён из ${source.id} в ${target.id}`);
});

События можно использовать для обновления состояния приложения или отправки данных на сервер после перемещения элементов.

Сортировка внутри контейнера

Для реализации сортировки Dragula автоматически переставляет элементы в контейнере при перетаскивании. Позиция элемента определяется положением курсора относительно соседних элементов (sibling).

drake.on('drop', function(el, target, source, sibling) {
  if (sibling) {
    target.insertBefore(el, sibling);
  } else {
    target.appendChild(el);
  }
});

Эта логика позволяет сохранять порядок элементов и динамически переставлять их в DOM без дополнительных вычислений.

Группировка контейнеров

Dragula поддерживает работу с несколькими контейнерами одновременно, что удобно для реализации интерфейсов с группировкой элементов по категориям. Группы контейнеров задаются массивом элементов при инициализации.

const groups = [
  document.getElementById('group1'),
  document.getElementById('group2'),
  document.getElementById('group3')
];

const drake = dragula(groups, {
  accepts: function (el, target, source, sibling) {
    return target.dataset.group === el.dataset.group; // только внутренняя группа
  }
});

Такой подход позволяет ограничить перемещение элементов только внутри одной группы, сохраняя структурированность данных.

Интеграция с динамическим контентом

Контейнеры и элементы не обязаны существовать при инициализации Dragula. Можно динамически добавлять элементы и контейнеры, после чего вызвать метод drake.containers.push(newContainer) для включения нового контейнера в обработку перетаскивания.

const newContainer = document.createElement('div');
newContainer.id = 'list3';
document.body.appendChild(newContainer);
drake.containers.push(newContainer);

Эта возможность особенно полезна для интерфейсов с подгрузкой контента по мере взаимодействия пользователя.

Стилизация элементов при перетаскивании

Dragula автоматически добавляет классы:

  • gu-mirror — копия элемента, следящая за курсором
  • gu-transit — перетаскиваемый элемент
  • gu-hide — скрывает оригинальный элемент при копировании

Дополнительно можно использовать CSS для изменения внешнего вида контейнеров и элементов в зависимости от состояния перетаскивания.

.gu-transit {
  opacity: 0.5;
  border: 1px dashed #000;
}
.gu-mirror {
  box-shadow: 2px 2px 5px rgba(0,0,0,0.3);
}

Оптимизация и производительность

Dragula минимально вмешивается в DOM и не создает лишних операций, что обеспечивает высокую производительность даже при большом количестве элементов. Для больших списков рекомендуется:

  • использовать requestAnimationFrame для любых дополнительных визуальных изменений,
  • избегать частого пересчета стилей внутри событий drag и drop,
  • использовать делегирование событий для контейнеров с динамическими элементами.

Практическая схема работы

  1. Создание контейнеров.
  2. Инициализация Dragula с настройками (moves, accepts, copy).
  3. Подписка на события для управления данными и интерфейсом.
  4. Настройка стилей для визуального отклика при перетаскивании.
  5. Добавление динамических элементов и контейнеров при необходимости.
  6. Контроль порядка и группировки элементов через события drop.

Эта структура позволяет создавать интерфейсы с интерактивными списками, перемещаемыми элементами и четкой группировкой без необходимости писать сложные алгоритмы сортировки вручную.