Debugging в production

Drag-and-drop интерфейсы на базе библиотеки Shopify Draggable активно используют DOM-манипуляции, события указателя, а также динамическое изменение структуры страницы. В production-окружении такие механизмы часто сталкиваются с проблемами, которые не проявляются в процессе локальной разработки.

Наиболее распространённые категории проблем:

1. Несоответствие DOM-структуры

Draggable зависит от стабильной структуры контейнеров и элементов. Если DOM динамически изменяется (например, через framework или сторонние скрипты), элементы могут перестать корректно обрабатываться библиотекой.

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

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

Если в runtime добавляются новые элементы:

container.innerHTML += '<div class="item">New</div>';

новый элемент не будет автоматически зарегистрирован в системе drag-and-drop.

Причина — Draggable инициализирует список draggable-элементов только при создании экземпляра.

Способы диагностики:

  • проверка текущего списка элементов
  • повторная инициализация
  • использование MutationObserver

Пример:

const observer = new MutationObserver(() => {
  draggable.destroy();
  initDraggable();
});

observer.observe(container, { childList: true });

Логирование событий библиотеки

В production-среде основным инструментом диагностики становится прослушивание внутренних событий.

Draggable генерирует большое количество событий:

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

Пример подключения логирования:

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

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

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

Для production-диагностики полезно добавлять условное логирование:

if (window.DEBUG_DRAG) {
  draggable.on('drag:move', e => console.log(e));
}

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


Проблемы с pointer events

Большинство ошибок drag-and-drop связаны с событиями указателя:

  • mousedown
  • touchstart
  • pointerdown

В production-среде они могут блокироваться:

  • CSS свойством pointer-events
  • overlay-элементами
  • сторонними UI-библиотеками

Диагностика начинается с проверки того, получает ли элемент событие.

Пример проверки:

document.addEventListener('pointerdown', e => {
  console.log(e.target);
});

Если событие не возникает, необходимо проверить:

.overlay {
  pointer-events: none;
}

или удалить перекрывающие элементы.


Конфликты с CSS

Некоторые CSS-свойства могут нарушать работу Draggable.

transform

Использование transform создаёт новый stacking context, что влияет на позиционирование drag-элемента.

Пример проблемного кода:

.container {
  transform: translateZ(0);
}

Это может вызывать смещение draggable-элемента относительно курсора.

overflow

Контейнеры с ограничением overflow могут скрывать draggable-элемент.

.container {
  overflow: hidden;
}

В production это приводит к ситуации, когда элемент визуально исчезает во время перемещения.

position

Некорректные комбинации:

position: relative
position: absolute
position: fixed

могут нарушать вычисление координат.


Ошибки при использовании Sortable

Модуль Sortable библиотеки Shopify Draggable добавляет дополнительную сложность.

Основные проблемы:

элементы меняют порядок, но DOM не обновляется

Причина — использование виртуального DOM (React, Vue и т.д.).

Draggable меняет DOM напрямую, а framework затем восстанавливает исходное состояние.

Пример проблемы:

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

После перемещения framework выполняет re-render и возвращает старый порядок.

Решение — синхронизация состояния.

Пример обработки:

sortable.on('sortable:sorted', (event) => {
  const newIndex = event.newIndex;
  const oldIndex = event.oldIndex;

  updateState(oldIndex, newIndex);
});

Race conditions

В production часто встречаются гонки состояний между:

  • drag событиями
  • DOM обновлениями
  • анимациями

Пример:

  1. пользователь начинает drag
  2. приложение обновляет DOM
  3. draggable-элемент исчезает

Симптомы:

  • drag внезапно прекращается
  • элементы дублируются
  • контейнер теряет draggable

Диагностика выполняется через:

  • заморозку состояния
  • логирование времени событий
draggable.on('drag:start', () => {
  window.dragActive = true;
});

draggable.on('drag:stop', () => {
  window.dragActive = false;
});

Проблемы сборки и минификации

В production код обычно проходит через:

  • bundlers
  • минификацию
  • tree shaking

Иногда это ломает библиотеку.

Частая проблема — удаление используемых плагинов.

Пример:

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

Если используется Sortable, но он не импортирован:

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

в production может возникнуть ошибка:

Sortable is not a constructor

Отладка через DevTools

Эффективная диагностика drag-поведения выполняется через инструменты браузера.

Breakpoints на события

В Chrome DevTools можно установить breakpoint на:

Event Listener Breakpoints → Mouse → mousedown

или

Pointer → pointerdown

Это позволяет определить, какой код перехватывает событие.


DOM breakpoints

Иногда draggable-элемент исчезает из DOM.

Для диагностики:

  1. выбрать элемент
  2. открыть вкладку Elements
  3. добавить breakpoint:
Break on → subtree modifications

DevTools остановит выполнение в момент изменения DOM.


Проблемы мобильных устройств

Drag-and-drop часто работает иначе на мобильных устройствах.

Основные причины:

  • пассивные event listeners
  • touch scrolling
  • жесты браузера

CSS-решение:

.draggable {
  touch-action: none;
}

Это отключает стандартные жесты браузера.


Memory leaks

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

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

function init() {
  new Draggable(container);
}

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

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

let draggable;

function init() {
  if (draggable) {
    draggable.destroy();
  }

  draggable = new Draggable(container);
}

Проверка состояния экземпляра

Иногда drag-система перестаёт реагировать на события.

Проверяется состояние экземпляра:

console.log(draggable);

Важно убедиться, что:

  • контейнеры существуют
  • draggable-селектор совпадает
  • экземпляр не уничтожен

Использование защитных проверок

Production-код должен учитывать возможные ошибки DOM.

Пример:

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

if (!container) {
  return;
}

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

Такие проверки предотвращают падение приложения при изменениях верстки.


Feature flags для drag-функциональности

В production-системах часто применяются feature flags.

Пример:

if (window.features.dragEnabled) {
  initDraggable();
}

Это позволяет:

  • отключать drag без деплоя
  • тестировать новые версии
  • быстро изолировать проблему

Метрики и мониторинг

Для сложных интерфейсов полезно собирать статистику drag-операций.

Пример:

draggable.on('drag:stop', () => {
  analytics.track('drag_completed');
});

Также фиксируются ошибки:

try {
  initDraggable();
} catch (error) {
  logError(error);
}

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


Восстановление после ошибок

Иногда drag-система может войти в неконсистентное состояние.

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

function resetDraggable() {
  draggable.destroy();
  draggable = new Draggable(containers, config);
}

Такой механизм позволяет автоматически восстанавливать функциональность без перезагрузки страницы.