SortableJS — мощная библиотека для реализации перетаскивания элементов на веб-странице. Помимо стандартных возможностей drag-and-drop, часто требуется поддержка отмены действий пользователя, особенно при работе с динамическими списками и сложными интерфейсами. Управление отменой операций в SortableJS требует понимания событийной модели библиотеки, работы с состоянием элементов и интеграции с внешними механизмами истории изменений.
Отмена перемещения элементов в SortableJS не реализована «из коробки» через встроенные методы. Существуют три основных подхода к реализации отката:
Хранение предыдущего состояния списка
Перед началом перетаскивания необходимо сохранить текущее состояние элементов. Обычно это массив объектов или идентификаторов элементов в порядке их расположения.
let previousOrder = [];
const sortable = new Sortable(listElement, {
onStart: () => {
previousOrder = Array.from(listElement.children).map(el => el.id);
}
});
Этот подход позволяет при необходимости восстановить порядок элементов:
function undo() {
previousOrder.forEach((id, index) => {
const element = document.getElementById(id);
listElement.appendChild(element);
});
}
Ключевой момент: важно сохранять идентификаторы элементов или уникальные свойства, чтобы корректно восстанавливать исходный порядок.
Использование событий onEnd и
onAdd
SortableJS предоставляет события onEnd (перетаскивание
завершено) и onAdd (элемент добавлен в новый список). Для
поддержки отмены в этих обработчиках можно сохранять информацию о старой
позиции элемента и о том, из какого списка он был перемещен:
const sortable = new Sortable(listElement, {
group: "shared",
onEnd: (evt) => {
const { oldIndex, newIndex, item } = evt;
previousOrder = { item, oldIndex, newIndex };
}
});
function undoLastMove() {
const { item, oldIndex, newIndex } = previousOrder;
if (oldIndex !== undefined) {
listElement.insertBefore(item, listElement.children[oldIndex]);
}
}
Этот метод подходит для пошаговой отмены последних перемещений.
Интеграция с системами хранения истории
Для сложных интерфейсов удобнее использовать стек изменений (history stack), куда записываются все операции пользователя:
const history = [];
const sortable = new Sortable(listElement, {
onEnd: (evt) => {
history.push({
itemId: evt.item.id,
from: evt.oldIndex,
to: evt.newIndex
});
}
});
function undo() {
const lastAction = history.pop();
if (!lastAction) return;
const item = document.getElementById(lastAction.itemId);
listElement.insertBefore(item, listElement.children[lastAction.from]);
}
Этот подход облегчает реализацию нескольких уровней отмены операций.
SortableJS позволяет объединять списки в группы (group),
что увеличивает сложность при реализации отмены. Основные аспекты:
pull и
put: если элемент может быть перемещен в другой
список, undo должен учитывать ограничения группы.Пример реализации undo для мультисписочного сценария:
const history = [];
const lists = document.querySelectorAll('.sortable-list');
lists.forEach(list => {
new Sortable(list, {
group: 'shared',
onEnd: evt => {
history.push({
itemId: evt.item.id,
fromList: evt.from,
toList: evt.to,
oldIndex: evt.oldIndex,
newIndex: evt.newIndex
});
}
});
});
function undo() {
const last = history.pop();
if (!last) return;
const item = document.getElementById(last.itemId);
last.fromList.insertBefore(item, last.fromList.children[last.oldIndex]);
}
При работе с динамическими данными часто требуется синхронизировать порядок элементов с серверной базой данных. Undo в этом случае может быть реализован через:
Важно учитывать, что если список синхронизируется с сервером, простое изменение DOM без уведомления сервера может вызвать рассинхронизацию данных.
Эти методы позволяют создать надёжную и гибкую систему отмены операций, полностью совместимую с функционалом SortableJS и динамическими веб-интерфейсами.