Древовидные структуры

SortableJS — это мощная библиотека на JavaScript, предназначенная для реализации перетаскивания элементов списка с минимальными усилиями. Она широко используется для создания интерактивных интерфейсов, где требуется возможность менять порядок элементов или строить сложные иерархические структуры.

Подключение и базовая инициализация

Для работы с SortableJS необходимо подключить библиотеку к проекту. Можно использовать CDN:

<script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.0/Sortable.min.js"></script>

Или установить через npm:

npm install sortablejs

Инициализация базового списка выглядит следующим образом:

const el = document.getElementById('list');
const sortable = new Sortable(el, {
    animation: 150,
    ghostClass: 'sortable-ghost'
});
  • animation — длительность анимации при перемещении элементов.
  • ghostClass — CSS-класс, применяемый к элементу во время перетаскивания.

Создание древовидной структуры

Для реализации древовидных структур используется концепция вложенных списков. Каждый элемент, который может содержать подсписок, оформляется как контейнер с вложенным <ul> или <ol>.

<ul id="tree">
    <li>
        Пункт 1
        <ul>
            <li>Подпункт 1.1</li>
            <li>Подпункт 1.2</li>
        </ul>
    </li>
    <li>Пункт 2</li>
</ul>

Каждый уровень иерархии можно инициализировать отдельным экземпляром SortableJS:

function makeSortable(el) {
    new Sortable(el, {
        group: 'nested',
        animation: 150,
        fallbackOnBody: true,
        swapThreshold: 0.65,
        onAdd: function (evt) {
            console.log('Элемент добавлен:', evt.item);
        }
    });

    Array.from(el.children).forEach(child => {
        const nested = child.querySelector('ul');
        if (nested) {
            makeSortable(nested);
        }
    });
}

makeSortable(document.getElementById('tree'));
  • group: 'nested' — позволяет элементам из разных списков одной группы быть перетаскиваемыми между собой.
  • fallbackOnBody — временно переносит элемент в <body> при перетаскивании, чтобы избежать проблем с overflow контейнеров.
  • swapThreshold — порог срабатывания при обмене элементов.

Работа с событиями

SortableJS предоставляет широкий набор событий для управления действиями пользователя:

  • onStart — срабатывает при начале перетаскивания.
  • onEnd — после завершения перетаскивания.
  • onAdd — элемент добавлен в новый список.
  • onUpdate — порядок элементов изменён в пределах одного списка.
  • onRemove — элемент удалён из списка.

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

new Sortable(listElement, {
    group: 'nested',
    onEnd: function (evt) {
        const updatedOrder = [];
        listElement.querySelectorAll('li').forEach(li => {
            updatedOrder.push(li.textContent.trim());
        });
        console.log('Обновлённый порядок:', updatedOrder);
    }
});

Особенности работы с вложенными списками

  1. Рекурсивная инициализация — каждый уровень списка необходимо обрабатывать отдельно, чтобы элементы подсписков также были перетаскиваемыми.
  2. Поддержка ограничений на глубину — можно управлять глубиной вложенности через проверку события onMove:
onMove: function (evt, originalEvent) {
    const target = evt.related;
    let depth = 0;
    let node = target;
    while (node && node !== document.body) {
        if (node.tagName === 'UL') depth++;
        node = node.parentNode;
    }
    return depth <= 3; // Максимальная глубина 3
}
  1. Стилизация элементов — класс ghostClass позволяет визуально отделять перетаскиваемый элемент. Класс chosenClass выделяет выбранный элемент до начала перемещения.

  2. Сериализация дерева — после перестановки элементов часто требуется сохранить иерархию. Для этого удобно использовать рекурсивную функцию:

function serializeTree(ul) {
    return Array.from(ul.children).map(li => {
        const nested = li.querySelector('ul');
        return {
            text: li.firstChild.textContent.trim(),
            children: nested ? serializeTree(nested) : []
        };
    });
}

const treeData = serializeTree(document.getElementById('tree'));
console.log(treeData);

Интеграция с другими фреймворками

SortableJS легко интегрируется с React, Vue и Angular через специализированные обёртки или кастомные компоненты. Основная концепция работы с DOM остаётся прежней: создаётся контейнер для элементов, к которому подключается Sortable, и события используются для синхронизации состояния с внутренними структурами данных.


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