Сохранение порядка элементов

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

<link rel="stylesheet" href="https://unpkg.com/dragula/dist/dragula.min.css">
<script src="https://unpkg.com/dragula/dist/dragula.min.js"></script>

После подключения можно создавать контейнеры и инициировать Dragula:

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

Основные принципы работы Dragula

Dragula управляет двумя ключевыми аспектами:

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

Важно понимать, что Dragula не изменяет исходные данные автоматически. Для сохранения порядка необходимо самостоятельно синхронизировать изменения с внутренними структурами данных (например, массивами или объектами).

События Dragula и их использование

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

  • drag — срабатывает в момент начала перетаскивания. Позволяет, например, добавлять визуальные эффекты.
  • drop — срабатывает при отпускании элемента в контейнере. Здесь можно фиксировать новый порядок элементов.
  • over / out — события при наведении на контейнер или уходе из него, полезны для подсветки зоны назначения.
  • cancel — срабатывает, если перетаскивание отменено.

Для сохранения порядка ключевое событие — drop:

drake.on('drop', (el, target, source, sibling) => {
    const order = Array.from(target.children).map(child => child.dataset.id);
    console.log('Новый порядок элементов:', order);
});

Здесь el — перемещаемый элемент, target — контейнер, в который был помещен элемент, source — исходный контейнер, sibling — элемент перед которым вставлен el. dataset.id — атрибут элемента, используемый для идентификации.

Работа с массивами данных

Для синхронизации DOM и массива данных порядок элементов в массиве нужно обновлять при событии drop. Пример с массивом объектов:

let items = [
    {id: '1', name: 'Элемент 1'},
    {id: '2', name: 'Элемент 2'},
    {id: '3', name: 'Элемент 3'}
];

function updateOrder(container) {
    const newOrder = Array.from(container.children).map(child => child.dataset.id);
    items.sort((a, b) => newOrder.indexOf(a.id) - newOrder.indexOf(b.id));
    console.log(items);
}

drake.on('drop', (el, target) => {
    updateOrder(target);
});

Такой подход обеспечивает согласованность интерфейса и данных.

Ограничение перетаскивания

Dragula позволяет ограничивать перемещение элементов по различным правилам:

  • moves — функция, определяющая, можно ли перемещать конкретный элемент. Например:
const drake = dragula(containers, {
    moves: (el, source, handle) => handle.classList.contains('handle')
});
  • accepts — функция, задающая правила вставки элемента в контейнер:
const drake = dragula(containers, {
    accepts: (el, target) => !target.classList.contains('no-drop')
});

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

Сохранение состояния между сессиями

Чтобы порядок элементов сохранялся после перезагрузки страницы, используется локальное хранилище (localStorage) или серверные запросы. Пример с localStorage:

drake.on('drop', (el, target) => {
    const order = Array.from(target.children).map(child => child.dataset.id);
    localStorage.setItem('itemOrder', JSON.stringify(order));
});

// Восстановление порядка при загрузке
const savedOrder = JSON.parse(localStorage.getItem('itemOrder') || '[]');
if (savedOrder.length) {
    const container = document.getElementById('list1');
    savedOrder.forEach(id => {
        const el = container.querySelector(`[data-id="${id}"]`);
        if (el) container.appendChild(el);
    });
}

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

Динамическое добавление и удаление элементов

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

function addItem(container, item) {
    const el = document.createElement('div');
    el.textContent = item.name;
    el.dataset.id = item.id;
    container.appendChild(el);
    updateOrder(container);
}

Удаление элемента:

function removeItem(container, id) {
    const el = container.querySelector(`[data-id="${id}"]`);
    if (el) el.remove();
    updateOrder(container);
}

Взаимодействие нескольких контейнеров

Dragula поддерживает работу с несколькими контейнерами одновременно. Перетаскивание между ними также обновляет порядок элементов в соответствующих массивах данных:

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

drake.on('drop', (el, target, source) => {
    updateOrder(source);
    updateOrder(target);
});

Таким образом сохраняется точная последовательность элементов в каждом контейнере.

Настройка визуальных эффектов

Для удобства пользователя часто добавляются стили при перетаскивании:

.gu-mirror {
    opacity: 0.7;
    background-color: #f0f0f0;
    border: 1px dashed #999;
}

.gu-transit {
    opacity: 0.5;
}

Эти классы автоматически применяются Dragula к перетаскиваемым элементам и зеркальной копии (mirror), создавая плавный визуальный эффект.

Поддержка адаптивного интерфейса

При использовании Dragula в адаптивном дизайне важно учитывать:

  • Перетаскивание на мобильных устройствах требует точного отслеживания касаний.
  • Можно отключать drag-and-drop на малых экранах с помощью условия в moves.
  • Визуальные подсказки должны масштабироваться с размерами контейнера и элементов.

Это позволяет сохранять порядок элементов независимо от устройства и разрешения экрана.