Как реализовать 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.
Discover how at OpenReplay.com.
Когда использовать 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.