API перетаскивания HTML можно использовать для создания различных пользовательских интерфейсов на наших веб-страницах. API состоит из восьми событий: drag, dragend, dragenter, dragexit, dragleave, dragover, dragstart и drop. В этом уроке мы собираемся создать демонстрационное приложение для опроса, которое предлагает пользователям ранжировать свои предпочтения вкусов мороженого, используя четыре из этих восьми событий. Исходный код этого руководства доступен на Github.

HTML-разметка выглядит следующим образом. Обратите внимание, что мы устанавливаем для атрибута draggable значение true для элементов, которые мы хотим перетащить. Если этот атрибут не установлен, его значение по умолчанию равно auto, что означает, что перетаскивать можно будет только выборки links, images и text.

Я использую таблицу стилей в дополнение к начальной загрузке для стилизации, но я сосредоточусь на коде Javascript. В начале скрипта у нас есть переменные, которые будут использоваться при реализации обработчиков событий перетаскивания.

Важными переменными для этой реализации являются dropZone, которые являются div, родительскими элементами label элементов. items — это массив объектных представлений label элементов (которые имеют класс item, как мы выбрали). Важно отметить, что document.querySelectorAll() возвращает объект NodeList, который представляет собой набор узлов DOM. Мы преобразуем NodeList в array (используя Array.from()), чтобы упростить повторение. Мы будем использовать submit и result во второй части урока.

Первое событие, которое мы будем обрабатывать, — это событие dragstart. Это срабатывает, когда пользователь начинает перетаскивать элемент. Мы отслеживаем selectedItem, который будет целью события перетаскивания (элемент, который мы перетаскиваем). Это глобальная переменная, потому что мы хотим иметь доступ к ней во всех событиях (на протяжении всего скрипта). В этом случае мы удаляем класс drop из класса selectedItem, потому что класс drop добавляется в более позднем событии перетаскивания. Так что, если, например, мы перетащим элемент метки, к элементу будет добавлен класс drop. Если бы мы хотели снова перетащить этот элемент, этот элемент по-прежнему имел бы класс drop, поэтому мы хотим удалить его в dragstart, чтобы мы могли добавить его снова, когда элемент будет удален.

Второе событие — это событие dragover. Несмотря на то, что это звучит как конец перетаскивания, это событие вызывается, когда выбранный элемент перетаскивается на цель перетаскивания, которой в нашем случае является dropZone. Во-первых, мы вызываем event.preventDefault().. По умолчанию браузер предотвращает что-либо при добавлении чего-либо в большинство HTML-элементов. Вызывая event.preventDefault() (и позже устанавливая прослушиватель событий для этих событий в dropZone element), мы указываем dropZone в качестве цели перетаскивания, поскольку элементы на странице по умолчанию не являются допустимыми целями перетаскивания.

Целью события dragover является элемент, ближайший к перетаскиваемому элементу. Если целевой элемент является одним из наших элементов метки, мы устанавливаем его как adjacentItem. Поскольку событие dragover срабатывает каждые несколько сотен миллисекунд, цель события также будет меняться в зависимости от координат перетаскиваемого элемента. У нас также есть prevAdjacentItem, который мы используем для отслеживания изменения соседнего элемента, когда происходит событие dragover. Например, если мы перетаскиваем один элемент, а затем перетаскиваем другой элемент перед перетаскиванием, мы хотим обновить переменную adjacentItem.

В первый раз adjacentItem не равно prevAdjacentItem (поскольку prevAdjacentItem не определено), поэтому второй оператор if не выполняется. Однако третье выражение if выполняется, поскольку adjacentItem не является нулевым, и поэтому adjacentItem получает верхнее значение margin из 4rem, так что создается впечатление, что оно движется, чтобы освободить место для selectedItem, если оно будет отброшено в этом месте. После срабатывания одного события мы устанавливаем prevAdjacentItem на adjacentItem. Поскольку мы не хотим, чтобы верхнее поле было 4rem постоянно, у нас есть функция resetStyles, которую мы вызываем, чтобы вернуть поля цели события в нормальное состояние. Мы вызываем эту функцию resetStyles при последующих запусках dragover. Поэтому, когда мы наводим курсор на разные элементы, верхнее поле каждого элемента сначала увеличивается, а затем возвращается к норме.

Третье событие, которое мы хотели бы обработать, это событие dragleave. Событие запускается, когда selectedItem перетаскивается от своего родителя или источника. Если пользователь выбирает элемент label и наводит курсор на другой элемент label, элемент label, который теперь является adjacentItem, будет иметь увеличенное верхнее поле. Если затем пользователь перетащит selectedItem из dropZone, мы также хотим сбросить стили adjacentItem , которые были установлены во время события dragover.

Наконец, событие drop запускается, когда selectedItem перебрасывается через допустимую цель перетаскивания. В данном случае целью перетаскивания является наш dropZone. Мы снова вызываем e.preventDefault(), чтобы предотвратить отбрасывание браузера по умолчанию. В этом случае мы используем функцию insertBefore(), чтобы вставить selectedItem перед adjacentItem, над которым он находился во время события dragover. Мы также добавляем класс drop, который представляет собой анимацию изменения цвета css. Затем мы вызываем resetStyles, чтобы сбросить верхнее поле adjacentItem.

Чтобы реагировать на эти события, мы затем устанавливаем прослушиватели событий на dropZone для событий drop, dragover и dragleave и на элементы label для события dragstart.

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