Drag'n'drop
Вбудований drag'n'drop
Можливість перетягувати мишкою об'єкти значно спростило інтерфейс і покращило UX (User Expirience).
Процес перетягування складається з декількох операцій:
- Захоплення об'єкта.
- Перетягування об'єкта.
- Відпускання об'єкта.
-
Перевірка контекста чи він може прийняти в себе об'єкт:
- якщо може - приймає в себе об'єкт;
- якщо не може - об'єкт повертається на своє місце.
HTML5 дав можливість використовувати 6 подій:
dragstart - початок перетягування;
dragenter - перетягування елемента на дропабельну область;
dragleave - драгабельний елемент вийшов за межі дропабельної області;
dragover - перетягування елемента над дропабельною областю;
drop - кидання елемента на дропабельну область;
dragend - подія завершення перетягування.
На практиці це виглядає так:
<div></div>
<span draggable="true">test</span>
div { margin-bottom: 16px; width: 200px; height: 120px; border: 1px solid red; }
span { display: inline-block; padding: 2px 10px; border: 1px solid green; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; cursor: move; }
.over { background: pink; }
let span = document.querySelector('span');
let div = document.querySelector('div');
span.addEventListener('dragstart', hDragStart);
span.addEventListener('dragend', hDragEnd);
div.addEventListener('dragenter', hDragEnter);
div.addEventListener('dragleave', hDragLeave);
div.addEventListener('dragover', hDragOver);
div.addEventListener('drop', hDrop);
function hDragStart(e) {
this.style.opacity = '0.5';
this.style.boxShadow = '0 0 3px rgba(0, 0, 0, 1)';
}
function hDragEnd(e) {
console.log('end');
div.classList.remove('over');
span.style.opacity = '';
span.style.boxShadow = '';
}
function hDragEnter(e) {
this.classList.add('over');
}
function hDragLeave(e) {
this.classList.remove('over');
}
function hDragOver(e) {
if (e.preventDefault) {
e.preventDefault();
}
e.dataTransfer.dropEffect = 'move';
console.log('move');
return false;
}
function hDrop(e) {
if (e.stopPropagation) {
e.stopPropagation();
}
console.log('drop');
return false;
}
В даному випадку у нас реалізовано лише механізм перетягування. Ніяких дій, окрім стилізації, не відбувається.
Ручний drag'n'drop
Вбудований drag'n'drop не дозволяє контролювати багато різних параметрів, наприклад, перетягування елемента лише в межах певної області.
Давайте спробуємо реалізувати перетягування в ручному режимі.
HTML-код та стилі візьмемо з попереднього експеремента.
В першу чергу заборонимо автоматичний drag'n'drop.
Далі реалізуємо події натискання та відпускання кнопки миші та наведення миші:
- при натисканні миші на span він стає драгабельним;
- при відпусканні миші на вікні span перестає бути драгабельним;
- при відпусканні миші на div він має прийняти span всередину себе.
.move { opacity: 0.5; boxShadow: 0 0 3px rgba(0, 0, 0, 1); }
let span = document.querySelector('span');
let div = document.querySelector('div');
span.ondragstart = function(){ return false; };
span.onmousedown = function(){
this.classList.add('move');
}
window.onmouseup = function(e){
span.classList.remove('move');
}
div.onmouseup = function(e){
if (!span.classList.contains('move')) { return false; }
this.appendChild(span);
}
Візуалізація перетягування об'єкта.
Добавимо візуалізацію елелемента, на який можна дропнути елемент, що перетягується.
div.onmouseenter = function(e){
if (!span.classList.contains('move')) { return false; }
this.classList.add('over');
}
div.onmouseleave = function(e){
this.classList.remove('over');
}
// і модифікуємо подію:
div.onmouseup = function(e){
this.classList.remove('over');
if (!span.classList.contains('move')) { return false; }
this.appendChild(span);
}
Тепер зробимо, щоб наш span тягався за мишою.
В першу чергу span перенесемо у body, якщо він має іншого батька.
Далі - при рухові мишки будемо переміщати наш елемент в нові координати.
.move { ... position: absolute; }
span.onmousedown = function(){
document.body.appendChild(this);
this.classList.add('move');
}
window.onmousemove = function(e){
// якщо в нас немає драгабельних елементів, то нічого не робимо:
if (!span.classList.contains('move')) { return false; }
span.style.left = e.pageX + 'px';
span.style.top = e.pageY + 'px';
}
// і модифікуємо подію:
div.onmouseup = function(e){
if (!span.classList.contains('move')) { return false; }
div.appendChild(span);
}
Як бачимо - є недоліки, при кліку на елемент - він відмальовується так, що його лівий верхній кут в позиції курсора.
Також подія div.onmouseup не відбувається (поясніть чому).
Окрім того, всі методи бажано реалізувати через EventListener.
Домашнє завдання
- Реалізуйте перетягування зображення по div.
- Створіть два блока, реалізуйте перетягування елементів між цими блоками.
- Дано блок, всередині якого input[type="file"]. Реалізуйте перетягування файла в блок з візуалізацією.
- Створіть плагін Sortable, подібний до jQuery UI Sortable.
learn.javascript.ru: Мышь: Drag'n'Drop.
habrahabr.ru: HTML5 Drag and Drop загрузка файлов.