SortableJS — это мощная библиотека для реализации перетаскивания и сортировки элементов на веб-странице. Одной из ключевых возможностей является клонирование элементов при перетаскивании, что позволяет создавать копии элементов вместо их перемещения. Такая функциональность востребована, например, при построении конструкторов интерфейса, списков товаров или интерактивных панелей.
cloneДля активации клонирования используется опция
sort: false и событие onClone. Основные
параметры:
const sortable = new Sortable(container, {
group: {
name: 'shared',
pull: 'clone', // элементы будут клонироваться при переносе
put: false
},
sort: false, // запрет перемещения внутри контейнера
onClone: function (evt) {
console.log('Элемент клонирован', evt.clone);
}
});
group.pull: 'clone' — указывает, что
элементы будут клонироваться при перетаскивании в другой контейнер.sort: false — отключает сортировку
внутри текущего контейнера, что важно для контейнеров-источников, где
элементы лишь копируются.onClone — коллбек, вызываемый при
создании клона элемента.SortableJS предоставляет несколько событий, полезных при работе с клонированными элементами:
onClone — вызывается сразу после
создания клона. Позволяет модифицировать клонированный элемент перед его
вставкой.onAdd — срабатывает при добавлении
элемента в новый контейнер. Полезно для установки уникальных
идентификаторов или дополнительных атрибутов.onRemove — срабатывает при удалении
элемента из исходного контейнера (не всегда вызывается при клонировании,
зависит от настроек pull).Пример использования событий:
const source = document.getElementById('source');
const target = document.getElementById('target');
new Sortable(source, {
group: { name: 'shared', pull: 'clone' },
sort: false,
onClone: (evt) => {
evt.clone.style.backgroundColor = '#f0f0f0';
}
});
new Sortable(target, {
group: { name: 'shared', put: true },
onAdd: (evt) => {
evt.item.textContent += ' (скопировано)';
}
});
В этом примере при перетаскивании элемента из source в
target создаётся его клон, которому изменяется фон, а при
вставке в target добавляется текст
(скопировано).
При клонировании часто требуется не только визуально скопировать
элемент, но и корректно обрабатывать его данные. Для этого можно
использовать data-* атрибуты или объекты, связанные с
элементами.
const items = [
{ id: 1, name: 'Элемент A' },
{ id: 2, name: 'Элемент B' }
];
const sourceContainer = document.getElementById('source');
items.forEach(item => {
const div = document.createElement('div');
div.textContent = item.name;
div.dataset.id = item.id;
sourceContainer.appendChild(div);
});
new Sortable(sourceContainer, {
group: { name: 'shared', pull: 'clone' },
sort: false,
onClone: (evt) => {
const originalId = evt.item.dataset.id;
evt.clone.dataset.id = `clone-${originalId}`;
}
});
Клонированные элементы получают уникальные идентификаторы, что упрощает дальнейшую работу с ними, например, сохранение в базе данных или отслеживание изменений.
SortableJS создаёт клон элемента с тем же HTML и стилями, что и оригинал. Для улучшения UX можно использовать:
setTimeout для анимации — позволяет
задерживать появление клона для плавного эффекта.sortable-ghost,
sortable-chosen помогают визуально выделить клонируемый
элемент.Пример с классами:
new Sortable(sourceContainer, {
group: { name: 'shared', pull: 'clone' },
sort: false,
onClone: (evt) => {
evt.clone.classList.add('clone-highlight');
}
});
.clone-highlight {
border: 2px dashed #007bff;
opacity: 0.8;
}
Клонирование элементов из контейнеров с вложенной структурой требует аккуратного обращения с DOM. SortableJS клонирует только конкретный элемент, без вложенных слушателей событий. Для сохранения функциональности необходимо заново инициализировать события на клоне или использовать делегирование через контейнер.
const nestedContainer = document.querySelector('.nested');
new Sortable(nestedContainer, {
group: { name: 'shared', pull: 'clone' },
sort: false,
onClone: (evt) => {
evt.clone.querySelectorAll('button').forEach(btn => {
btn.addEventListener('click', () => alert('Нажата кнопка в клоне'));
});
}
});
Это позволяет сохранить интерактивность клонов, даже если исходные элементы содержат динамические обработчики.
По умолчанию клонируется только сам элемент с его содержимым
(innerHTML). Для более сложных случаев можно:
cloneNode(true),
для глубокого копирования всего DOM-дерева элемента.onClone: (evt) => {
const deepClone = evt.item.cloneNode(true);
evt.clone.replaceWith(deepClone);
}
Это особенно полезно для интерактивных виджетов, где стандартное клонирование теряет привязанные скрипты и состояния.