Библиотека Draggable из экосистемы Shopify используется для реализации перетаскивания элементов интерфейса. Несмотря на относительно простой API, при интеграции часто возникают проблемы, связанные с DOM-структурой, событиями, CSS-оформлением и взаимодействием с другими библиотеками.
Раздел содержит систематизированный разбор наиболее распространённых ошибок и практические методы их диагностики.
Одна из самых частых проблем — библиотека не запускается и элементы не реагируют на перетаскивание.
1. Неверный селектор контейнера
const draggable = new Draggable('.container', {
draggable: '.item'
});
Если .container отсутствует в DOM на момент
инициализации, библиотека не создаст экземпляр.
console.log(document.querySelector('.container'));
Если результат null, контейнер либо отсутствует, либо
ещё не загружен.
Инициализация после загрузки DOM:
document.addEventListener('DOMContentLoaded', () => {
const draggable = new Draggable('.container', {
draggable: '.item'
});
});
2. Скрипт библиотеки не подключён
Проверка в DevTools:
Uncaught ReferenceError: Draggable is not defined
Подключение через CDN:
<script src="https://cdn.jsdelivr.net/npm/@shopify/draggable/lib/draggable.bundle.js"></script>
Либо через пакетный менеджер:
npm install @shopify/draggable
Импорт в модульной системе:
import {Draggable} from '@shopify/draggable';
Ситуация, когда библиотека инициализирована, но перемещение элементов не происходит.
Draggable требует правильной вложенности:
container
├── item
├── item
└── item
Пример:
<ul class="list">
<li class="item">A</li>
<li class="item">B</li>
<li class="item">C</li>
</ul>
new Draggable('.list', {
draggable: '.item'
});
Если элемент .item находится вне контейнера,
перетаскивание не будет работать.
Некоторые CSS-свойства блокируют drag-события.
pointer-events: none;
user-select: none;
overflow: hidden;
Также часто мешает:
position: fixed
или
transform: translate()
у родительских элементов.
В DevTools:
Это типично при использовании Sortable плагина поверх Draggable.
Пример неправильной конфигурации:
import {Draggable} from '@shopify/draggable';
Draggable сам по себе не сортирует элементы, он только перемещает их.
import {Sortable} from '@shopify/draggable';
new Sortable('.list', {
draggable: '.item'
});
Плагин Sortable отвечает за перестановку элементов.
Иногда элемент остаётся в состоянии drag даже после отпускания мыши.
Пример проблемного кода:
draggable.on('drag:start', (event) => {
event.source.remove();
});
Удаление исходного элемента ломает внутреннюю логику.
Изменение DOM после события drag:stop.
draggable.on('drag:stop', (event) => {
event.source.remove();
});
После первого перемещения элементы перестают реагировать.
Повторный рендер DOM.
Это характерно для фреймворков:
Если список полностью перерисовывается, старый экземпляр Draggable привязан к устаревшим DOM-узлам.
Повторная инициализация:
draggable.destroy();
draggable = new Sortable('.list', {
draggable: '.item'
});
Некоторые браузеры блокируют drag-события из-за поведения touch-интерфейсов.
touch-scroll конфликтует с drag.
Добавление CSS:
.item {
touch-action: none;
}
Или:
.list {
-webkit-user-drag: element;
}
Происходит резкий сдвиг элемента в момент начала drag.
Draggable создаёт mirror element — копию элемента для визуального перемещения.
Если у элемента сложные CSS-правила, mirror может иметь другие размеры.
Проверка класса:
draggable-mirror
Настройка mirror-стилей:
.draggable-mirror {
box-sizing: border-box;
width: inherit;
}
Draggable не ограничивает перемещение по умолчанию.
Использование плагина Snappable.
import {Draggable, Snappable} from '@shopify/draggable';
new Draggable('.container', {
draggable: '.item',
plugins: [Snappable]
});
Draggable предоставляет систему событий:
drag:start
drag:move
drag:stop
sortable:start
sortable:sorted
Иногда обработчики не вызываются.
Использование неправильного экземпляра.
Неверно:
document.addEventListener('drag:start', handler);
Правильно:
draggable.on('drag:start', handler);
Ситуация:
container
└── card
└── draggable element
Draggable может захватывать родительский контейнер.
Использование handle.
new Draggable('.container', {
draggable: '.card',
handle: '.card-header'
});
Перетаскивание будет активироваться только через
.card-header.
Часто конфликтует с:
Проблема возникает из-за перекрытия событий:
mousedown
mousemove
mouseup
Проверка слушателей:
getEventListeners(element)
в DevTools.
Изоляция области:
event.stopPropagation();
или удаление конфликтующих библиотек.
При динамических интерфейсах экземпляры Draggable могут оставаться в памяти.
Не вызывается destroy().
draggable.destroy();
Это удаляет:
Если Draggable используется внутри Shadow DOM, стандартные селекторы не находят элементы.
Пример:
shadowRoot.querySelector('.container')
Передача DOM-элемента напрямую:
new Draggable(containerElement, {
draggable: '.item'
});
При работе с большими списками (1000+ элементов) могут появляться лаги.
Минимизация repaint:
.item {
will-change: transform;
}
Упрощение DOM-структуры.
Использование virtualized lists.
При использовании flex-контейнеров элементы иногда неправильно меняют позицию.
display: flex
flex-wrap: wrap
Draggable ориентируется на порядок DOM, а не на визуальный layout.
Использование grid-layout или перерасчёт индексов после сортировки.
Эффективная диагностика включает несколько техник.
draggable.on('drag:start', (event) => {
console.log(event);
});
Позволяет увидеть:
console.log(draggable);
В объекте доступны:
В DOM во время drag появляется:
<div class="draggable-mirror"></div>
Отсутствие mirror часто указывает на CSS-конфликт.
Основные инструменты:
Это позволяет выявлять:
1. Изолированная инициализация
Draggable должен инициализироваться только один раз на контейнер.
2. Контроль жизненного цикла
При удалении компонента всегда вызывать:
destroy()
3. Минимизация CSS-конфликтов
Стили drag-элементов должны быть максимально простыми.
4. Ограничение области drag
Использование:
handle
cancel
distance
для контроля поведения.
5. Логирование событий
Позволяет быстро обнаружить некорректные сценарии работы.
Системный подход к диагностике, проверка DOM-структуры, анализ событийной модели и контроль жизненного цикла экземпляров Draggable позволяют устранить большинство ошибок, возникающих при разработке интерфейсов с поддержкой drag-and-drop.