Back

Как реализовать Drag and Drop в Svelte

Как реализовать Drag and Drop в Svelte

Drag and drop кажется простым, пока вы не попытаетесь его реализовать. Браузер предоставляет нативный API, но он имеет реальные ограничения: отсутствие плавных анимаций, непоследовательная поддержка сенсорных устройств и непредсказуемое поведение в разных браузерах. Если вы когда-либо наблюдали, как элементы неловко встают на место при перетаскивании, вы знаете проблему.

Это руководство охватывает два практических подхода к реализации drag and drop в Svelte — использование нативного HTML5 API и использование библиотеки — чтобы вы могли выбрать правильный инструмент для того, что вы действительно создаёте.

Ключевые выводы

  • Нативный HTML5 Drag and Drop API подходит для простой сортировки списков без зависимостей, но не имеет плавных анимаций, поддержки сенсорных устройств и последовательного кроссбраузерного поведения.
  • Руны Svelte 5 ($state()) и синтаксис атрибута ondragstart упрощают реактивный drag and drop по сравнению со Svelte 3/4.
  • Для анимированного, мультисписочного, сенсорного или доступного drag and drop svelte-dnd-action является практичным выбором библиотеки.
  • События перетаскивания работают только на клиенте — в SvelteKit с SSR защищайте логику перетаскивания с помощью onMount или проверки browser.

Понимание двух подходов

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

Нативный HTML5 Drag and Drop API использует встроенные в браузер события: dragstart, dragover, dragenter, dragleave и drop. Он не требует зависимостей и хорошо работает для простых случаев использования. Компромисс заключается в том, что анимации требуют ручной работы, поддержка сенсорных устройств непоследовательна на разных устройствах, а визуальная обратная связь во время перетаскивания ограничена. Вы можете узнать больше об API в документации MDN по Drag and Drop.

Библиотечные решения, такие как svelte-dnd-action или Neodrag, обрабатывают сложные части — плавные FLIP-анимации, поддержку сенсорных устройств и доступные взаимодействия — из коробки. Они добавляют небольшую стоимость бандла, но экономят значительное время реализации для чего-либо, выходящего за рамки базового сортируемого списка.

Реализация Drag and Drop в Svelte 5 с нативным API

Вот чистый пример переупорядочивания одного списка с использованием синтаксиса рун Svelte 5:

<script>
  let items = $state(['Svelte', 'SvelteKit', 'Vite', 'TypeScript']);
  let dragIndex = $state(null);

  function handleDragStart(event, index) {
    dragIndex = index;
    event.dataTransfer.effectAllowed = 'move';
  }

  function handleDragOver(event, index) {
    event.preventDefault();
    if (dragIndex === null || dragIndex === index) return;

    const updated = [...items];
    const [moved] = updated.splice(dragIndex, 1);
    updated.splice(index, 0, moved);

    items = updated;
    dragIndex = index;
  }

  function handleDragEnd() {
    dragIndex = null;
  }
</script>

<ul>
  {#each items as item, index (item)}
    <li
      draggable="true"
      class:dragging={dragIndex === index}
      ondragstart={(e) => handleDragStart(e, index)}
      ondragover={(e) => handleDragOver(e, index)}
      ondragend={handleDragEnd}
    >
      {item}
    </li>
  {/each}
</ul>

<style>
  li {
    padding: 10px 16px;
    margin: 6px 0;
    background: #f1f1f1;
    cursor: grab;
    list-style: none;
    border-radius: 4px;
  }
  .dragging {
    opacity: 0.4;
  }
</style>

Несколько моментов, заслуживающих внимания в этом паттерне Svelte 5:

  • $state() заменяет старые объявления реактивных переменных из Svelte 3/4.
  • Svelte 5 также поддерживает синтаксис атрибута ondragstart в дополнение к традиционной директиве события on:dragstart.
  • Список обновляется во время перетаскивания (на dragover), а не только при отпускании — это даёт пользователям визуальную обратную связь в реальном времени.

Примечание по SvelteKit: События перетаскивания работают только на клиенте. Если вы используете SvelteKit с SSR, оберните любую логику, связанную с перетаскиванием, в onMount или защитите её проверкой browser из $app/environment.

Когда использовать svelte-dnd-action вместо этого

Нативный подход работает для простых сценариев переупорядочивания списков. Но как только вам понадобится что-либо из следующего, обратитесь к svelte-dnd-action:

  • Плавные FLIP-анимации между позициями списка
  • Многосписочный drag and drop (доски в стиле Kanban)
  • Поддержка сенсорных и мобильных устройств без дополнительного кода
  • Доступные клавиатурные взаимодействия встроены

Паттерн svelte-dnd-action прямолинейный — вы применяете действие use:dndzone к вашему контейнеру, передаёте массив элементов и обрабатываете события consider и finalize для обновления состояния. Каждый элемент должен иметь уникальное свойство id. Соедините его со встроенной в Svelte анимацией flip, и вы получите производственное качество взаимодействий перетаскивания менее чем в 20 строках.

ПотребностьИспользуйте
Простое переупорядочивание списка, без анимацийНативный API
Плавные анимации, мультисписок, сенсорные устройстваsvelte-dnd-action
Свободное перетаскивание элементовNeodrag

Заключение

Для базового переупорядочивания списков с drag and drop в Svelte нативный браузерный API с рунами Svelte 5 доставит вас туда без зависимостей. Для чего-либо более сложного — анимированных Kanban-досок, поддержки сенсорных устройств или доступных взаимодействий — svelte-dnd-action является практичным выбором. Начните с нативного подхода, чтобы понять механику, затем переходите на библиотеку, когда ваши требования этого потребуют.

Часто задаваемые вопросы

Не надёжно. Поддержка событий перетаскивания на сенсорных устройствах непоследовательна в мобильных браузерах. Для правильной поддержки мобильных устройств вам нужен либо полифил, такой как mobile-drag-drop, либо библиотека, такая как svelte-dnd-action, которая нативно обрабатывает сенсорные взаимодействия.

Да. svelte-dnd-action работает со Svelte 5. Вы объявляете массив элементов с помощью $state() и обновляете его внутри обработчиков событий consider и finalize. Директива use:dndzone остаётся той же. Просто убедитесь, что каждый элемент в вашем массиве имеет уникальное свойство id, чтобы библиотека могла правильно отслеживать элементы.

Браузер генерирует призрачное изображение по умолчанию из внешнего вида перетаскиваемого элемента. У вас ограниченный контроль над этим с нативным API. Вы можете настроить его, используя event.dataTransfer.setDragImage() для предоставления пользовательского элемента или снимка canvas, но для полного визуального контроля во время перетаскивания библиотека, такая как svelte-dnd-action или Neodrag, является лучшим выбором.

Многосписочный drag and drop с нативным API требует ручного отслеживания исходных и целевых контейнеров, что быстро становится сложным. svelte-dnd-action упрощает это, позволяя вам применять use:dndzone к каждому контейнеру списка и использовать один и тот же тип элемента в разных зонах. Элементы автоматически перемещаются между списками при перетаскивании через контейнеры.

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay