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

Библиотека Shopify Draggable предоставляет мощный и гибкий инструмент для реализации перетаскиваемых элементов на веб-страницах. Один из ключевых сценариев — работа с динамически добавляемыми элементами, когда набор draggable-элементов изменяется во время выполнения приложения.

Инициализация Draggable для динамических элементов

При стандартной инициализации Draggable создается экземпляр для набора существующих элементов:

import { Draggable } from '@shopify/draggable';

const draggable = new Draggable(document.querySelectorAll('.draggable-item'), {
  draggable: '.draggable-item'
});

Если новые элементы добавляются в DOM после инициализации, они не станут автоматически draggable. Необходимо обновить или пересоздать экземпляр Draggable, либо использовать метод addContainer.

Использование addContainer для новых элементов

Метод addContainer позволяет подключить новый контейнер с элементами к существующему экземпляру Draggable:

const newContainer = document.querySelector('#new-container');
draggable.addContainer(newContainer);

Все элементы внутри #new-container, соответствующие селектору draggable, становятся частью drag-and-drop логики без полной перезагрузки Draggable.

Полная пересборка Draggable

Иногда удобнее уничтожить текущий экземпляр и создать новый с обновленным списком элементов:

draggable.destroy();

const allItems = document.querySelectorAll('.draggable-item');
const newDraggable = new Draggable(allItems, {
  draggable: '.draggable-item',
  delay: 100
});

Это особенно полезно, когда новые элементы добавляются динамически в разные контейнеры или меняются настройки (например, класс draggable или задержка drag).

События для динамических элементов

Draggable поддерживает стандартные события, такие как drag:start, drag:move, drag:stop, а также sortable:sorted. Для динамических элементов их обработка требует делегирования событий:

draggable.on('drag:start', (event) => {
  console.log('Начало перетаскивания:', event.source);
});

draggable.on('sortable:sorted', (event) => {
  console.log('Элемент отсортирован:', event.oldIndex, '→', event.newIndex);
});

После добавления новых элементов через addContainer или пересоздание экземпляра события автоматически привязываются ко всем draggable-элементам.

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

Для динамических списков часто важно сохранять порядок элементов и поддерживать корректные позиции:

  • Использование sortable:sorted позволяет отслеживать перемещения и обновлять внутренние данные.
  • Для элементов, добавленных динамически, важно учитывать размеры и маргины, так как Draggable вычисляет координаты исходя из текущего layout.

Пример обновления данных при добавлении нового элемента:

const container = document.querySelector('#list');
const newItem = document.createElement('div');
newItem.classList.add('draggable-item');
newItem.textContent = 'Новый элемент';
container.appendChild(newItem);

draggable.addContainer(container); // делает новый элемент draggable

draggable.on('sortable:sorted', (event) => {
  const items = Array.from(container.children).map(el => el.textContent);
  console.log('Обновленный порядок:', items);
});

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

  • Для больших динамических списков рекомендуется пакетное добавление элементов, а не добавлять по одному, чтобы минимизировать перерасчет Draggable.
  • При удалении элементов следует использовать removeContainer или destroy, чтобы избежать утечек памяти и некорректных ссылок.
  • Для сложных интерфейсов с множеством контейнеров лучше создавать один экземпляр Draggable с несколькими контейнерами, чем много отдельных экземпляров.

Поддержка nested контейнеров

Draggable поддерживает вложенные контейнеры, что особенно полезно для динамически добавляемых блоков:

const parentContainer = document.querySelector('#parent');
const childContainer = document.querySelector('#child');

draggable.addContainer(parentContainer);
draggable.addContainer(childContainer);

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

Заключение по динамическому добавлению

Ключевой подход заключается в аккуратном управлении контейнерами и пересоздании экземпляра Draggable при значительных изменениях DOM. Методы addContainer и destroy обеспечивают гибкость для динамических интерфейсов, сохраняя возможность отслеживания событий и контроля порядка элементов.

Использование этих инструментов позволяет строить сложные drag-and-drop интерфейсы с динамическими списками, карточками и nested контейнерами, сохраняя высокую отзывчивость и стабильность приложения.