Русский
Русский
English
Статистика
Реклама

Как реализовать dragampdrop на чистом JavaScript

Вэтом туториале мырассмотрим, как реализовать эффект drag&drop наванильном JavaScript. Дословный перевод санглийского потяни ибрось отражает суть эффекта, это хорошо знакомое любому пользователю перетаскивание элементов интерфейса.

Drag &drop может понадобиться вразных ситуациях например, втаких:

  • Простое визуальное изменение положения элемента.

  • Сортировка элементов спомощью перетаскивания. Пример сортировка карточек задач втаск-трекере.

  • Изменение контекста элемента. Пример перенос задачи втаск-трекере изодного списка вдругой.

  • Перемещение локальных файлов вокно браузера.

Мыразберём drag &drop напримере сортировки. Для этого создадим интерактивный список задач.

HTML Drag and Drop API

Встандарте HTML5 есть API, который позволяет реализовать эффект drag &drop. Ондаёт возможность спомощью специальных событий контролировать захват элемента настранице мышью иего перемещение вновое положение. Рассмотрим этот API подробнее.

Поумолчанию перемещаться могут только ссылки, изображения ивыделенные фрагменты. Если начать перетаскиватьих, появится фантомная копия, которая будет следовать закурсором. Чтобы добавить возможность перетаскивания другим элементам, нужно задать атрибуту draggable значение true.

<div draggable="true">Draggable element</div>

Далее для реализации перемещения используется ряд событий, которые срабатывают наразных этапах. Полный список есть наMDN, амырассмотрим основные.

  • drag срабатывает каждые несколько сотен миллисекунд, пока элемент перетаскивается.

  • dragstart срабатывает вмомент начала перетаскивания элемента.

  • dragend срабатывает вмомент, когда перетаскивание элемента завершено.

  • dragover срабатывает каждые несколько сотен миллисекунд, пока перетаскиваемый элемент находится над зоной, вкоторую может быть сброшен.

  • drop срабатывает втот момент, когда элемент будет брошен, если онможет быть перемещён втекущую зону.

При успешном перемещении элемент должен оказаться нановом месте. Нопоумолчанию большинство областей настранице недоступны для сброса. Чтобы создать область, вкоторую элементы могут быть сброшены, необходимо слушать событие dragover или drop нанужном элементе иотменять действие поумолчанию спомощью метода preventDefault. Тогда стандартное поведение будет переопределено перетаскивание исброс вэту область станут возможными. Рассмотрим напримере чуть позже.

Это далеко невсе возможности API. Также внём есть несколько интерфейсов, которые помогают получать доступ кданным перетаскиваемого элемента иизменятьих. Например, существует объект DataTransfer, который кроме всего прочего хранит информацию олокальных файлах, если они перетаскиваются. Вэтом туториале нам непонадобится использовать эти возможности, нобез DataTransfer необойтись, если нужно, например, загружать файлы скомпьютера исчитывать данные, чтобы затем производить сними какие-либо манипуляции. Подробно обэтом наMDN.

Приступим ксозданию нашего списка задач ирассмотрим напримере, как работать сHTML Drag and Drop API.

Вёрстка истилизация списка задач

Список будет состоять изнескольких задач изаголовка. Для начала создадим разметку. Здесь всё просто если речь идёт осписке, значит нужен тег ul.

<section class="tasks">  <h1 class="tasks__title">To do list</h1>  <ul class="tasks__list">    <li class="tasks__item">learn HTML</li>    <li class="tasks__item">learn CSS</li>    <li class="tasks__item">learn JavaScript</li>    <li class="tasks__item">learn PHP</li>    <li class="tasks__item">stay alive</li>  </ul></section>

Теперь добавим элементам базовую стилизацию:

body {  font-family: "Tahoma", sans-serif;  font-size: 18px;  line-height: 25px;  color: #164a44;  background-color: #b2d9d0;}.tasks__title {  margin: 50px 0 20px 0;  text-align: center;  text-transform: uppercase; }.tasks__list {  margin: 0;  padding: 0;  list-style: none;}.tasks__item {  transition: background-color 0.5s;  margin-bottom: 10px;  padding: 5px;  text-align: center;  border: 2px dashed #b2d9d0;  border-radius: 10px;  cursor: move;  background-color: #dff2ef;  transition: background-color 0.5s;}.tasks__item:last-child {  margin-bottom: 0;}.selected {  opacity: 0.6;}

Здесь стоит обратить внимание натип курсора, который мыуказали move. Все элементы списка смогут перемещаться, аспомощью курсора move мыподсказываем пользователю, что есть такая возможность.

Также мызадали стилизацию для класса selected, который чуть позже будем добавлять программно при взаимодействии сэлементом

Реализация drag &drop

Шаг1. Разрешим перетаскивание элементов

Переходим кJavaScript. Впервую очередь присвоим элементам упомянутый ранее атрибут draggable созначением true, чтобы разрешить задачам перемещаться. Это можно сделать прямо вразметке или спомощью JavaScript.

const tasksListElement = document.querySelector(`.tasks__list`);const taskElements = tasksListElement.querySelectorAll(`.tasks__item`);// Перебираем все элементы списка и присваиваем нужное значениеfor (const task of taskElements) {  task.draggable = true;}

Уже сейчас перетаскивание доступно для элементов, нопока это выражается только впоявлении фантомной копии. Своего положения элементы неменяют, добавим перемещение чуть позже.

Шаг2. Добавим реакцию наначало иконец перетаскивания

Будем отслеживать события dragstart и dragend навсём списке. Вначале перетаскивания будем добавлять класс selected элементу списка, накотором было вызвано событие. После окончания перетаскивания будем удалять этот класс.

tasksListElement.addEventListener(`dragstart`, (evt) => {  evt.target.classList.add(`selected`);})tasksListElement.addEventListener(`dragend`, (evt) => {  evt.target.classList.remove(`selected`);});

Шаг3. Реализуем логику перетаскивания

Мыдобрались доосновной части перетаскивания задач. Будем отслеживать местоположение перемещаемого элемента относительно других, подписавшись насобытие dragover. Благодаря тому, что оно срабатывает очень часто, мысможем налету вставлять элемент внужное место взависимости отположения курсора. Для этого реализуем такую логику:

  1. Делаем всю область списка доступной для сброса.

  2. Находим выбранный элемент .selected итот элемент, накотором сработало событие dragover.

  3. Проверяем, что событие dragover сработало ненавыбранном элементе, потому что иначе перемещать элемент нет смысла онуже нанужном месте.

  4. Также проверяем, что dragover сработало именно наодном изэлементов списка. Это важно, потому что курсор может оказаться инапустом пространстве между элементами, аоно нас неинтересует.

  5. Находим элемент, перед которым нужно осуществить вставку. Сделаем это, сравнив положение выбранного элемента итекущего, накоторый наведён курсор.

  6. Вставляем выбранный элемент нановое место.

Напишем код:

tasksListElement.addEventListener(`dragover`, (evt) => {  // Разрешаем сбрасывать элементы в эту область  evt.preventDefault();  // Находим перемещаемый элемент  const activeElement = tasksListElement.querySelector(`.selected`);  // Находим элемент, над которым в данный момент находится курсор  const currentElement = evt.target;  // Проверяем, что событие сработало:  // 1. не на том элементе, который мы перемещаем,  // 2. именно на элементе списка  const isMoveable = activeElement !== currentElement &&    currentElement.classList.contains(`tasks__item`);  // Если нет, прерываем выполнение функции  if (!isMoveable) {    return;  }  // Находим элемент, перед которым будем вставлять  const nextElement = (currentElement === activeElement.nextElementSibling) ?      currentElement.nextElementSibling :      currentElement;  // Вставляем activeElement перед nextElement  tasksListElement.insertBefore(activeElement, nextElement);});

Для поиска nextElement мыиспользовали тернарный оператор. Если выещё сним незнакомы, это можно исправить, прочитав статью.

Вцелом получившийся наэтом этапе код рабочий. Уже сейчас элементы можно сортировать так, как мыипланировали. Нопри этом уварианта есть недостаток перемещаемый элемент меняет положение втот момент, когда курсор попадает надругой элемент. Такое поведение недостаточно оптимально истабильно. Сточки зрения пользователя логичнее ориентироваться нацентр элемента. Тоесть мыдолжны осуществлять вставку только после того, как курсор пересечёт центральную ось, анесразу после наведения наэлемент. Чтобы реализовать это поведение, напишем функцию для получения nextElement другим способом.

Шаг4. Учтём положение курсора относительно центра

Функция должна принимать навход вертикальную координату курсора итекущий элемент, накотором сработало событие dragover. Мыбудем сравнивать текущее положение курсора сцентральной осью элемента, над которым курсор находится вмомент перетаскивания. Таким образом, если мыхотим поменять элементы местами, товставка должна сработать втот момент, когда курсор пересекает центральную ось. Значит нас интересуют вертикальные координаты курсора ицентра элемента, над которым оннаходится.

Давайте создадим функцию getNextElement(). Мыуже знаем, что она должна возвращать тот элемент, перед которым нужно сделать вставку. Вэтом нам поможет координата курсора итекущий элемент, которые будут переданы впараметры.

Чтобы получить вертикальную координату текущего элемента, используем метод getBoundingClientRect(). Онвызывается наэлементе ивозвращает объект, всвойствах которого находится информация оразмерах икоординатах элемента относительно вьюпорта. Нам понадобится координата y, нотакже нужно будет учесть высоту элемента height, потому что у рассчитывается относительно верхнего левого угла элемента, анам нужен центр.

const getNextElement = (cursorPosition, currentElement) => {  // Получаем объект с размерами и координатами  const currentElementCoord = currentElement.getBoundingClientRect();  // Находим вертикальную координату центра текущего элемента  const currentElementCenter = currentElementCoord.y + currentElementCoord.height / 2;  // Если курсор выше центра элемента, возвращаем текущий элемент  // В ином случае  следующий DOM-элемент  const nextElement = (cursorPosition < currentElementCenter) ?      currentElement :      currentElement.nextElementSibling;  return nextElement;};

Давайте закрепим напримере. Допустим, мыхотим поменять два элемента местами начинаем перемещать нижний элемент, наводим курсор наэлемент перед ним. Пока мынеприблизились кцентру элемента, ничего происходить недолжно, потому что пока порядок элементов вDOM изменять ненужно. Нокак только курсор пересечёт центральную ось, перемещаемый элемент будет вставлен перед тем элементом, накоторый мынавели курсор.

Всё почти готово, нонам нужно ещё учесть ситуацию, когда вовремя перемещения курсор был наведён накакой-то элемент ипри этом центральную ось так инепересёк. Для нас это значит, что порядок неизменился, иничего делать ненадо. Нопрограмма пока обэтом незнает ивтаких ситуациях осуществляет вставку вDOM натоже самое место при каждом срабатывании события dragover. Как мыпомним, оно срабатывает очень часто икаждый раз влечёт засобой ненужные операции сDOM. Мыизменим это поведение, добавив проверку.

tasksListElement.addEventListener(`dragover`, (evt) => {  evt.preventDefault();  const activeElement = tasksListElement.querySelector(`.selected`);  const currentElement = evt.target;  const isMoveable = activeElement !== currentElement &&    currentElement.classList.contains(`tasks__item`);  if (!isMoveable) {    return;  }  // evt.clientY  вертикальная координата курсора в момент,  // когда сработало событие  const nextElement = getNextElement(evt.clientY, currentElement);  // Проверяем, нужно ли менять элементы местами  if (    nextElement &&     activeElement === nextElement.previousElementSibling ||    activeElement === nextElement  ) {    // Если нет, выходим из функции, чтобы избежать лишних изменений в DOM    return;  }  tasksListElement.insertBefore(activeElement, nextElement);});

Теперь всё работает так, как нужно: мыотслеживаем положение курсора относительно центра, лишние операции вDOM исключилии, главное, элементы сортируются задача выполнена! Полный код решения внашей интерактивной демонстрации.

Полезности


В HTML Academy обновилась профессия React-разработчик теперь в программе очень много программирования на JavaScript и React.js и бесконечное количество практики. Там не только список задач, целый космический корабль можно сделать. После обучения проверка и прокачка навыков на реальных проектах и оплачиваемая стажировка на коммерческих заказах. 95% выпускников профессии трудоустраиваются. Старт 27 апреля.

Источник: habr.com
К списку статей
Опубликовано: 11.02.2021 16:05:53
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Блог компании html academy

Разработка веб-сайтов

Javascript

Перетаскивание

Drag&amp;drop

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru