Библиотека Shopify Draggable построена вокруг событийной архитектуры. Любое действие пользователя — начало перетаскивания, перемещение элемента, вход в контейнер или завершение операции — сопровождается генерацией события.
Система событий позволяет:
Каждый объект перетаскивания выступает источником событий и реализует
механизм подписки через метод on.
Каждый экземпляр Draggable предоставляет API для подписки на события.
Базовый синтаксис:
const draggable = new Draggable(document.querySelectorAll('.container'), {
draggable: '.item'
});
draggable.on('drag:start', (event) => {
console.log('Начало перетаскивания', event);
});
Метод on() принимает:
Обработчик получает объект события, содержащий подробную информацию о текущем состоянии операции.
События библиотеки делятся на несколько логических групп:
Относятся к процессу перетаскивания.
Основные события:
| Событие | Описание |
|---|---|
drag:start |
начало перетаскивания |
drag:move |
перемещение элемента |
drag:stop |
завершение операции |
При перетаскивании создаётся «зеркальный» элемент — визуальная копия объекта, которая следует за курсором.
События:
| Событие | Описание |
|---|---|
mirror:create |
создание зеркала |
mirror:move |
перемещение зеркала |
mirror:destroy |
удаление зеркала |
Отслеживают взаимодействие с контейнерами.
| Событие | Описание |
|---|---|
drag:over |
элемент перемещён над контейнером |
drag:out |
элемент покинул контейнер |
drag:over:container |
наведение на контейнер |
Если используется модуль сортировки, появляются дополнительные события:
| Событие | Описание |
|---|---|
sortable:start |
начало сортировки |
sortable:sort |
изменение позиции |
sortable:sorted |
элемент перемещён |
Каждый обработчик получает объект события со структурой:
draggable.on('drag:start', (event) => {
console.log(event.source);
console.log(event.originalSource);
console.log(event.container);
});
Основные поля:
| Поле | Назначение |
|---|---|
source |
текущий перетаскиваемый элемент |
originalSource |
исходный DOM-элемент |
container |
контейнер |
originalEvent |
нативное DOM событие |
mirror |
зеркальный элемент |
Логирование удобно использовать для отладки.
Пример отслеживания жизненного цикла:
draggable.on('drag:start', (event) => {
console.log('Drag started', event.source);
});
draggable.on('drag:move', (event) => {
console.log('Dragging', event.source);
});
draggable.on('drag:stop', (event) => {
console.log('Drag finished', event.source);
});
В консоли можно наблюдать последовательность действий пользователя.
При работе с большим количеством событий удобнее создать универсальный логгер.
const logEvent = (name) => (event) => {
console.log(`[${name}]`, event);
};
draggable.on('drag:start', logEvent('drag:start'));
draggable.on('drag:move', logEvent('drag:move'));
draggable.on('drag:stop', logEvent('drag:stop'));
Преимущества подхода:
Можно зарегистрировать сразу несколько обработчиков:
const events = [
'drag:start',
'drag:move',
'drag:stop',
'mirror:create',
'mirror:destroy'
];
events.forEach((eventName) => {
draggable.on(eventName, (event) => {
console.log(eventName, event);
});
});
Такой подход полезен при:
Для полноценной диагностики полезно выводить ключевые параметры события.
draggable.on('drag:start', (event) => {
console.log({
element: event.source,
container: event.container,
x: event.originalEvent.clientX,
y: event.originalEvent.clientY
});
});
Получаемые данные:
Отслеживание координат позволяет анализировать поведение пользователя.
draggable.on('drag:move', (event) => {
const { clientX, clientY } = event.originalEvent;
console.log(`Move: ${clientX}, ${clientY}`);
});
Такая информация применяется в:
Отслеживание переходов между контейнерами важно при сложных интерфейсах.
draggable.on('drag:over:container', (event) => {
console.log('Entered container', event.overContainer);
});
И при выходе:
draggable.on('drag:out', (event) => {
console.log('Left container', event.container);
});
Зеркало (mirror) создаётся автоматически при начале
перетаскивания.
draggable.on('mirror:create', (event) => {
console.log('Mirror created', event.mirror);
});
Удаление:
draggable.on('mirror:destroy', (event) => {
console.log('Mirror removed');
});
Это позволяет:
Для предотвращения утечек памяти обработчики необходимо удалять.
function handleStart(event) {
console.log('Start', event);
}
draggable.on('drag:start', handleStart);
draggable.off('drag:start', handleStart);
Метод off():
Для глубокого анализа можно подписаться на максимальное количество событий.
const allEvents = [
'drag:start',
'drag:move',
'drag:stop',
'drag:over',
'drag:out',
'mirror:create',
'mirror:move',
'mirror:destroy'
];
allEvents.forEach((name) => {
draggable.on(name, (event) => {
console.log(`[Draggable] ${name}`, event);
});
});
Такой режим используется при:
Логи можно отправлять во внешние системы.
Пример:
function sendLog(data) {
fetch('/logger', {
method: 'POST',
body: JSON.stringify(data)
});
}
draggable.on('drag:start', (event) => {
sendLog({
type: 'drag_start',
element: event.source.id
});
});
Это позволяет:
Избыточное логирование может влиять на производительность, особенно
при событиях drag:move, которые вызываются десятки раз в
секунду.
Методы оптимизации:
1. Дебаунс
let lastLog = 0;
draggable.on('drag:move', (event) => {
const now = Date.now();
if (now - lastLog > 200) {
console.log('Drag move');
lastLog = now;
}
});
2. Фильтрация событий
draggable.on('drag:move', (event) => {
if (!event.source.classList.contains('loggable')) {
return;
}
console.log('Tracked element move');
});
3. Условное логирование
const DEBUG = true;
draggable.on('drag:start', (event) => {
if (DEBUG) {
console.log(event);
}
});
Часто используется следующая структура:
drag:start
drag:move
drag:over
drag:out
drag:stop
mirror:destroy
Это позволяет проследить полный цикл:
Логи формируют детальную временную линию взаимодействия пользователя с интерфейсом.