DOM Advanced

События мыши и Drag'n'Drop

Работать с событиями мыши (click, mousedown, mousemove, mouseup), координатами (clientX/Y) и HTML Drag and Drop API.

Продвинутый (Advanced)

Кратко

События мыши: click (клик), mousedown (нажатие), mouseup (отпускание), mousemove (движение). Координаты: clientX/Y (от viewport), pageX/Y (от страницы). Drag and Drop: dragstart → dragover → drop. dataTransfer передаёт данные между элементами.


Подробно

События мыши:

  • click — полный клик (mousedown + mouseup)
  • mousedown — кнопка нажата
  • mouseup — кнопка отпущена
  • mousemove — движение мыши (срабатывает часто!)
  • dblclick — двойной клик

Координаты:

element.addEventListener('mousemove', (e) => { console.log('clientX:', e.clientX, 'clientY:', e.clientY); console.log('pageX:', e.pageX, 'pageY:', e.pageY); });

clientX/Y — от начала viewport (окна браузера) pageX/Y — от начала страницы (с учётом прокрутки)

Drag and Drop API:

  • dragstart — начало перетаскивания
  • drag — элемент перетаскивается
  • dragenter — элемент вошёл в drop-зону
  • dragover — элемент над drop-зоной
  • dragleave — элемент покинул drop-зону
  • drop — элемент dropped в зону
  • dragend — перетаскивание завершено

Пример:

// Draggable элемент const draggable = document.querySelector('.draggable'); draggable.addEventListener('dragstart', (e) => { e.dataTransfer.setData('text/plain', draggable.id); e.dataTransfer.effectAllowed = 'move'; });

// Drop-зона const dropZone = document.querySelector('.drop-zone'); dropZone.addEventListener('dragover', (e) => { e.preventDefault(); // обязательно! dropZone.classList.add('over'); });

dropZone.addEventListener('dragleave', () => { dropZone.classList.remove('over'); });

dropZone.addEventListener('drop', (e) => { e.preventDefault(); const id = e.dataTransfer.getData('text/plain'); const element = document.getElementById(id); dropZone.appendChild(element); });


Ментальная модель

Drag and Drop — как перестановка мебели. Вы берёте предмет (dragstart), несёте его (drag), подносите к новому месту (dragover), и ставите (drop). dataTransfer — как этикетка на предмете: на ней написано, что это за предмет.


Примеры

**Самый простой:**

javascript
const box = document.querySelector('.box');

box.addEventListener('mousedown', () => console.log('Нажал'));
box.addEventListener('mouseup', () => console.log('Отпустил'));
box.addEventListener('click', () => console.log('Клик'));
// При клике: Нажал → Отпустил → Клик

Порядок событий: mousedown → mouseup → click.

**Простой:**

javascript
const area = document.querySelector('.area');
const dot = document.querySelector('.dot');

let isDragging = false;

area.addEventListener('mousedown', (e) => {
  isDragging = true;
});

document.addEventListener('mousemove', (e) => {
  if (!isDragging) return;
  dot.style.left = e.clientX + 'px';
  dot.style.top = e.clientY + 'px';
});

document.addEventListener('mouseup', () => {
  isDragging = false;
});

Перетаскивание через mouse events (не Drag and Drop API).

**Реальный пример:**

javascript
// HTML5 Drag and Drop
const items = document.querySelectorAll('.item');
const container = document.querySelector('.container');

items.forEach(item => {
  item.draggable = true;

  item.addEventListener('dragstart', (e) => {
    e.dataTransfer.setData('text/plain', item.id);
    item.classList.add('dragging');
  });

  item.addEventListener('dragend', () => {
    item.classList.remove('dragging');
  });
});

container.addEventListener('dragover', (e) => {
  e.preventDefault();
  const afterElement = getDragAfterElement(container, e.clientY);
  const dragging = document.querySelector('.dragging');
  if (afterElement == null) {
    container.appendChild(dragging);
  } else {
    container.insertBefore(dragging, afterElement);
  }
});

Реальный Drag and Drop: сортировка списка с определением позиции.


Частые ошибки

**Неправильно:** Забывать e.preventDefault() в dragover

**Почему:** По умолчанию браузер НЕ позволяет drop. Нужно отменить default action в dragover.

**Правильно:** В dragover всегда вызывайте e.preventDefault().

**Неправильно:** Использовать mousemove без throttle/debounce

**Почему:** mousemove срабатывает десятки раз в секунду — это может замедлить страницу.

**Правильно:** Для анимаций используйте requestAnimationFrame. Для редких обновлений — throttle.

**Неправильно:** Путать clientX и pageX

**Почему:** clientX — от viewport. pageX — от страницы. При прокрутке они различаются.

**Правильно:** Для позиционирования элемента используйте clientX/Y. Для позиции на странице — pageX/Y.


Важно запомнить

  • mousedown → mouseup → click (порядок событий)
  • clientX/Y — от viewport, pageX/Y — от страницы
  • mousemove срабатывает часто — будьте аккуратны
  • Drag and Drop: dragstart → dragover → drop
  • В dragover обязательно e.preventDefault()
  • dataTransfer передаёт данные между drag и drop

Связь

Назад: Вы знаете события (DA1-DA5). Теперь вы работаете с конкретными взаимодействиями мыши и перетаскиванием.

Вперёд: Следующий урок (DA7) — взаимодействие с формами: свойства, методы и события.

Материалы курса DOM Advanced — PROlab Academy.