Частые ошибки

Одна из наиболее распространённых проблем при работе с библиотекой Shopify Draggable связана с некорректным созданием экземпляра класса. Библиотека требует точного указания контейнера и списка элементов, которые должны участвовать в перетаскивании.

Типичная ошибка заключается в передаче неправильного селектора контейнера.

const draggable = new Draggable('.container', {
  draggable: '.item'
});

Если селектор .container отсутствует в DOM или выбран неправильно, библиотека не сможет привязать обработчики событий.

Корректная проверка существования контейнера:

const container = document.querySelector('.container');

if (container) {
  const draggable = new Draggable(container, {
    draggable: '.item'
  });
}

Ключевые моменты:

  • контейнер должен существовать в DOM на момент инициализации
  • элементы внутри контейнера должны соответствовать селектору draggable
  • инициализация должна происходить после загрузки DOM

Пример безопасной инициализации:

document.addEventListener('DOMContentLoaded', () => {
  const draggable = new Draggable('.container', {
    draggable: '.item'
  });
});

Ошибки в структуре DOM

Библиотека чувствительна к структуре разметки. Неправильная вложенность элементов часто приводит к непредсказуемому поведению.

Пример проблемной структуры:

<div class="container">
  <div class="wrapper">
    <div class="item">1</div>
    <div class="item">2</div>
  </div>
</div>

Если указано:

draggable: '.item'

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

Более стабильная структура:

<div class="container">
  <div class="item">1</div>
  <div class="item">2</div>
</div>

Особенно это важно при использовании модулей сортировки и перемещения между контейнерами.


Подключение только части модулей

Shopify Draggable построена на модульной архитектуре. Основные классы:

  • Draggable
  • Sortable
  • Droppable
  • Swappable

Распространённая ошибка — использование функциональности модуля, который не был импортирован.

Пример ошибки:

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

const sortable = new Sortable(...);

Класс Sortable не будет доступен.

Правильный импорт:

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

Или использование полного набора:

import { Draggable, Sortable, Droppable } from '@shopify/draggable';

В среде сборщиков (Webpack, Vite) необходимо следить за корректностью импортируемых модулей.


Неправильная работа с событиями библиотеки

Библиотека предоставляет развитую систему событий:

  • drag:start
  • drag:move
  • drag:stop
  • sortable:start
  • sortable:sorted

Ошибка часто возникает из-за неверного имени события.

Некорректный код:

draggable.on('dragStart', (event) => {
  console.log(event);
});

В библиотеке используется формат namespace:event.

Правильная запись:

draggable.on('drag:start', (event) => {
  console.log(event);
});

Дополнительная ошибка — попытка обратиться к данным события вне его контекста.

let draggedItem;

draggable.on('drag:start', (event) => {
  draggedItem = event.source;
});

Важно учитывать, что event.source представляет исходный DOM-элемент.


Игнорирование CSS-ограничений

Некоторые стили могут полностью блокировать работу перетаскивания.

Распространённые проблемы:

overflow: hidden

.container {
  overflow: hidden;
}

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

pointer-events: none

.item {
  pointer-events: none;
}

Элемент перестаёт реагировать на события мыши.

user-select

Рекомендуется отключать выделение текста:

.item {
  user-select: none;
}

Ошибки при работе с динамически добавляемыми элементами

После инициализации библиотеки новые элементы могут не участвовать в перетаскивании.

Пример:

container.insertAdjacentHTML(
  'beforeend',
  '<div class="item">New</div>'
);

Если элемент добавлен динамически, необходимо убедиться, что он соответствует селектору draggable.

Библиотека работает через делегирование событий, поэтому повторная инициализация обычно не требуется. Однако ошибки могут возникнуть при:

  • изменении структуры DOM
  • замене контейнера
  • удалении элементов

Опасный сценарий:

container.innerHTML = '';

При полном перерисовывании контейнера рекомендуется заново создать экземпляр Draggable.


Конфликт с другими drag-and-drop библиотеками

Использование нескольких библиотек перетаскивания одновременно может приводить к конфликтам событий.

Особенно часто проблемы возникают при одновременном использовании:

  • jQuery UI
  • SortableJS
  • нативного HTML5 drag-and-drop

Причины конфликтов:

  • одинаковые события mousedown
  • блокировка event.preventDefault()
  • изменение DOM во время перетаскивания

Лучшей практикой является использование только одного drag-and-drop решения в пределах одного интерфейсного модуля.


Неправильная конфигурация handle

Опция handle ограничивает область, за которую можно начать перетаскивание.

Ошибка:

const draggable = new Draggable('.container', {
  draggable: '.item',
  handle: '.drag'
});

Если внутри .item отсутствует элемент .drag, перетаскивание не будет запускаться.

Правильная структура:

<div class="item">
  <span class="drag">☰</span>
  Card
</div>

Игнорирование зеркального элемента (mirror)

Во время перетаскивания библиотека создаёт зеркальный элемент — копию перетаскиваемого блока.

Этот элемент размещается в document.body.

Иногда это вызывает проблемы:

  • стили применяются только внутри контейнера
  • позиционирование ломается

Ошибка:

.container .item {
  width: 200px;
}

Зеркальный элемент находится вне .container, поэтому стиль не применяется.

Решение:

.item {
  width: 200px;
}

Неправильная работа с несколькими контейнерами

При использовании Sortable или Droppable можно перемещать элементы между контейнерами.

Ошибка возникает, если передан только один контейнер.

Некорректно:

new Sortable(document.querySelector('.container'), {
  draggable: '.item'
});

Для межконтейнерной сортировки нужно передать массив:

new Sortable(document.querySelectorAll('.container'), {
  draggable: '.item'
});

Пример структуры:

<div class="container"></div>
<div class="container"></div>

Потеря состояния после сортировки

После перемещения элементов разработчики часто пытаются обновить состояние вручную, что приводит к рассинхронизации.

Неправильный подход:

draggable.on('sortable:sorted', () => {
  saveOrder();
});

Если порядок элементов считывается до завершения DOM-обновления, результат может быть некорректным.

Лучший момент для чтения состояния — событие завершения:

draggable.on('drag:stop', () => {
  const items = [...document.querySelectorAll('.item')];
});

Ошибки при уничтожении экземпляра

При удалении компонентов интерфейса (например, в SPA-приложениях) необходимо корректно уничтожать экземпляр библиотеки.

Ошибка:

container.remove();

Обработчики событий остаются в памяти.

Правильный подход:

draggable.destroy();
container.remove();

Метод destroy():

  • удаляет все обработчики
  • очищает плагины
  • освобождает ресурсы

Это особенно важно при работе с фреймворками интерфейса и частых перерисовках интерфейса.