В этом руководстве вы узнаете об API Drag and Drop в JavaScript и о том, как использовать его для реализации простого приложения перетаскивания элементов.
HTML5 официально представил спецификацию перетаскивания. В большинстве современных веб-браузеров реализовано собственное перетаскивание на основе спецификации HTML5.
По умолчанию можно перетаскивать только изображение и текст. Чтобы перетащить изображение, просто нажмите и удерживайте кнопку мыши, а затем переместите его. Чтобы перетащить текст, вам нужно выделить текст и перетащить его так же, как перетаскивание изображения.
Спецификация HTML5 указывает, что почти все элементы можно перетаскивать. Чтобы сделать элемент перетаскиваемым, вы добавляете свойство draggable
со значением true
в его HTML-тег. Например:
<div class="item" draggable="true"></div>
События для перетаскиваемых элементов
Когда вы перетаскиваете элемент, эти события срабатывают в следующей последовательности:
dragstart
drag
dragend
Когда вы удерживаете кнопку мыши и начинаете двигать мышь, событие dragstart
срабатывает на перетаскиваемом элементе. Курсор изменится на символ запрета на отбрасывание (круг с перечеркнутой линией), чтобы указать, что вы не можете отбросить элемент сам по себе.
После запуска события dragstart
событие drag
запускается постоянно, пока вы перетаскиваете элемент.
И событие dragend
срабатывает, когда вы перестаете переносить элемент.
Целью всех событий( e.target
) является перетаскиваемый элемент.
По умолчанию браузер не меняет внешний вид. Таким образом, вы можете настроить его внешний вид в соответствии со своими предпочтениями.
События для целей перетаскивания
Когда вы перетаскиваете элемент по допустимой цели, эти события срабатывают в следующей последовательности:
dragenter
dragover
dragleave
илиdrop
Событие dragenter
срабатывает, как только вы перетаскиваете элемент на цель.
После того, как событие dragenter
срабатывает, событие dragover
срабатывает постоянно, пока вы перетаскиваете элемент в пределах границы целевого объекта.
Когда вы перетаскиваете элемент за границу целевого объекта dragover
перестает запускаться и dragleave
становится событием перетаскивания.
Если вы помещаете элемент в цель, вместо события dragleave
запускается событие drop
.
Целью( e.target
) dragenter
, dragover
, dragleave
и drop
являются элементы цели.
Действительная цель
Почти все элементы поддерживают события перетаскивания ( dragenter
, dragover
, dragleave
и drop
). Однако по умолчанию они не позволяют сбрасывать.
Если вы перетащите элемент на цель перетаскивания, которая не позволяет перетаскивать, событие drop
не сработает.
Чтобы превратить элемент в допустимую цель перетаскивания, вы можете переопределить поведение по умолчанию событий dragenter
и dragover
, вызвав метод event.preventDefault()
в соответствующих обработчиках событий.(Дополнительную информацию см. в разделе примеров)
Передача данных с помощью объекта dataTransfer
Для передачи данных в действии перетаскивания используется объект dataTransfer
.
Объект dataTransfer
является свойством события. Он позволяет передавать данные из перетаскиваемого элемента в цель перетаскивания.
Объект dataTransfer
имеет два метода: setData()
и getData()
.
setData()
позволяет вам установить данные операции в указанный формат и данные:
dataTransfer.setData(format, data)
Формат может быть text/plain
или text/uri-list
. И данные могут быть строкой, представляющей данные для добавления к объекту перетаскивания.
Метод getData()
извлекает данные, сохраненные методом setData()
.
getData()
принимает один аргумент:
dataTransfer(format)
Формат может быть text/plain
или text/uri-list
. getData()
возвращает строку, сохраненную методом setData()
, или пустую строку, если операция перетаскивания не включает данные.
Пример
Мы разработаем следующее простое приложение перетаскивания, чтобы продемонстрировать API перетаскивания JavaScript:
Создайте структуру проекта
- Сначала создайте новую папку с именем
drag-n-drop-basics
. Внутри этой папки создайте две подпапки сcss
иjs
. - Во-вторых, создайте новый файл с именем
app.js
в папкеjs
,style.css
в папкеcss
иindex.html
в папкеdrag-n-drop-basics
. - В-третьих, поместите ссылку на
style.css
и тег script, который ссылается наapp.js
, в файлеindex.html
следующим образом:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>JavaScript - Drag and Drop Demo</title> <link rel="stylesheet" href="css/style.css"> </head> <body> <script src="js/app.js"></script> </body> </html>
Создайте файл index.html
Поместите следующий код в файл index.html
:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>JavaScript - Drag and Drop Demo</title> <link rel="stylesheet" href="css/style.css"> </head> <body> <div class="container"> <h1>JavaScript - Drag and Drop</h1> <div class="drop-targets"> <div class="box"> <div class="item" id="item"> </div> </div> <div class="box"></div> <div class="box"></div> </div> </div> <script src="js/app.js"></script> </body> </html>
В этом файле index.html мы использовали элемент .container
для выравнивания заголовка и элемента drop-targets
.
Внутри элемента drop-targets мы поместили три элемента div
с одинаковым классом box
. И мы помещаем еще один элемент div с item
класса в первое поле.
Если вы откроете index.html
и попытаетесь перетащить желтое поле, вы увидите курсор, указывающий, что вы не можете перетаскивать:
Чтобы сделать элемент перетаскиваемым, вы добавляете свойство draggable
со значением true
в его HTML-тег следующим образом:
<div class="item" id="item" draggable="true">
Теперь, если вы сохраните index.html
, снова откроете его в браузере, вы увидите, что вы можете перетаскивать элемент элемента следующим образом:
Обработка событий
В файле style.css
есть класс .hide
, скрывающий элемент:
.hide { display: none; }
В файл app.js
вы добавляете следующий код:
// select the item element const item = document.querySelector('.item'); // attach the dragstart event handler item.addEventListener('dragstart', dragStart); // handle the dragstart function dragStart(e) { console.log('drag starts...'); }
Как это работает:
- Сначала выберите перетаскиваемый элемент с помощью
querySelector()
. - Во-вторых, прикрепите обработчик события
dragstart
к перетаскиваемому элементу. - В-третьих, определите
dragStart()
для обработкиdragstart
.
Если вы откроете файл index.html и начнете перетаскивать элемент, вы увидите сообщение о drag starts...
в консоли.
В обработчике события dragStart
вам нужно сохранить id
перетаскиваемого элемента. И вам нужно скрыть это:
function dragStart(e) { e.dataTransfer.setData('text/plain', e.target.id); e.target.classList.add('hide'); }
Если вы перетащите элемент, вы увидите, что он исчезнет, как только вы начнете перетаскивать.
Чтобы решить эту проблему, вы используете функцию setTimeout()
:
function dragStart(e) { e.dataTransfer.setData('text/plain', e.target.id); setTimeout(() => { e.target.classList.add('hide'); }, 0); }
Теперь вы можете перетащить перетаскиваемый элемент из исходного положения:
Обработка событий
В файле style.css также есть класс CSS с именем .drag-over
, который превращает стиль границы цели перетаскивания в пунктирную и красную:
.drag-over { border: dashed 3px red; }
В app.js вам нужно выбрать целевые элементы перетаскивания и обработать dragenter
, dragover
, dragleave
и drop
этих элементов:
const boxes = document.querySelectorAll('.box'); boxes.forEach(box => { box.addEventListener('dragenter', dragEnter) box.addEventListener('dragover', dragOver); box.addEventListener('dragleave', dragLeave); box.addEventListener('drop', drop); }); function dragEnter(e) { } function dragOver(e) { } function dragLeave(e) { } function drop(e) { }
Стиль границы цели перетаскивания должен измениться, когда происходит событие dragenter
и dragover
. Стиль должен восстанавливаться при возникновении события dragleave
and drop
.
Для этого вы добавляете и удаляете класс drag-over
в цель drop
следующим образом:
function dragEnter(e) { e.target.classList.add('drag-over'); } function dragOver(e) { e.target.classList.add('drag-over'); } function dragLeave(e) { e.target.classList.remove('drag-over'); } function drop(e) { e.target.classList.remove('drag-over'); }
Теперь, если вы перетащите перетаскиваемый элемент на другую цель перетаскивания, вы увидите, что граница цели изменится, как показано на следующем рисунке:
Чтобы сделать цель перетаскивания действительной, вам нужно вызвать event.preventDefault()
в обработчиках событий dragenter
и dragover
следующим образом:
function dragEnter(e) { e.preventDefault(); e.target.classList.add('drag-over'); } function dragOver(e) { e.preventDefault(); e.target.classList.add('drag-over'); }
Если вы этого не сделаете, событие drop
никогда не сработает, потому что элемент div
по умолчанию не является допустимой целью перетаскивания.
Если вы перетащите элемент в цель перетаскивания, вы увидите, что курсор изменится, указывая на то, что вы можете перетащить элемент:
Теперь, если вы отбросите элемент, вы увидите, что он немедленно исчезает.
Чтобы решить эту проблему, вам нужно добавить дескриптор события drop
.
- Сначала получите
id
перетаскиваемого элемента с помощью методаgetData()
объектаdataTransfer
. - Во-вторых, добавьте перетаскиваемый элемент в качестве дочернего элемента целевого элемента перетаскивания.
- В-третьих, удалите класс
hide
изdraggable
элемента.
В следующем коде показан полный обработчик события drop
в JavaScript:
function drop(e) { e.target.classList.remove('drag-over'); // get the draggable element const id = e.dataTransfer.getData('text/plain'); const draggable = document.getElementById(id); // add it to the drop target e.target.appendChild(draggable); // display the draggable element draggable.classList.remove('hide'); }
Если вы перетащите перетаскиваемый элемент сейчас, он должен работать должным образом.
Ниже показан полный файл app.js:
/* draggable element */ const item = document.querySelector('.item'); item.addEventListener('dragstart', dragStart); function dragStart(e) { e.dataTransfer.setData('text/plain', e.target.id); setTimeout(() => { e.target.classList.add('hide'); }, 0); } /* drop targets */ const boxes = document.querySelectorAll('.box'); boxes.forEach(box => { box.addEventListener('dragenter', dragEnter) box.addEventListener('dragover', dragOver); box.addEventListener('dragleave', dragLeave); box.addEventListener('drop', drop); }); function dragEnter(e) { e.preventDefault(); e.target.classList.add('drag-over'); } function dragOver(e) { e.preventDefault(); e.target.classList.add('drag-over'); } function dragLeave(e) { e.target.classList.remove('drag-over'); } function drop(e) { e.target.classList.remove('drag-over'); // get the draggable element const id = e.dataTransfer.getData('text/plain'); const draggable = document.getElementById(id); // add it to the drop target e.target.appendChild(draggable); // display the draggable element draggable.classList.remove('hide'); }
Заключение
- Добавьте свойство
draggable
со значением true к элементу, чтобы сделать его перетаскиваемым в JavaScript. - События
dragstart
,drag
иdragend
срабатывают на перетаскиваемом элементе. - События
dragenter
,dragover
,dragleave
илиdrop
срабатывают на цели. - Вызовите
event.preventDefault()
в обработчиках событийdragenter
иdragover
, чтобы сделать элемент допустимой целью перетаскивания. - Используйте объект
event.dataTransfer
с методамиsetData()
иgetData()
для передачи данных в операции.