Метод drag and drop в ворде
В этом туториале мы рассмотрим, как реализовать эффект drag&drop на ванильном JavaScript. Дословный перевод с английского — «потяни и брось» — отражает суть эффекта, это хорошо знакомое любому пользователю перетаскивание элементов интерфейса.
Drag & drop может понадобиться в разных ситуациях — например, в таких:
Простое визуальное изменение положения элемента.
Сортировка элементов с помощью перетаскивания. Пример — сортировка карточек задач в таск-трекере.
Изменение контекста элемента. Пример — перенос задачи в таск-трекере из одного списка в другой.
Перемещение локальных файлов в окно браузера.
Мы разберём drag & drop на примере сортировки. Для этого создадим интерактивный список задач.
HTML Drag and Drop API
В стандарте HTML5 есть API, который позволяет реализовать эффект drag & drop. Он даёт возможность с помощью специальных событий контролировать захват элемента на странице мышью и его перемещение в новое положение. Рассмотрим этот API подробнее.
По умолчанию перемещаться могут только ссылки, изображения и выделенные фрагменты. Если начать перетаскивать их, появится фантомная копия, которая будет следовать за курсором. Чтобы добавить возможность перетаскивания другим элементам, нужно задать атрибуту draggable значение true .
Далее для реализации перемещения используется ряд событий, которые срабатывают на разных этапах. Полный список есть на MDN, а мы рассмотрим основные.
drag — срабатывает каждые несколько сотен миллисекунд, пока элемент перетаскивается.
dragstart — срабатывает в момент начала перетаскивания элемента.
dragend — срабатывает в момент, когда перетаскивание элемента завершено.
dragover — срабатывает каждые несколько сотен миллисекунд, пока перетаскиваемый элемент находится над зоной, в которую может быть сброшен.
drop — срабатывает в тот момент, когда элемент будет брошен, если он может быть перемещён в текущую зону.
При успешном перемещении элемент должен оказаться на новом месте. Но по умолчанию большинство областей на странице недоступны для сброса. Чтобы создать область, в которую элементы могут быть сброшены, необходимо слушать событие dragover или drop на нужном элементе и отменять действие по умолчанию с помощью метода preventDefault . Тогда стандартное поведение будет переопределено — перетаскивание и сброс в эту область станут возможными. Рассмотрим на примере чуть позже.
Это далеко не все возможности API. Также в нём есть несколько интерфейсов, которые помогают получать доступ к данным перетаскиваемого элемента и изменять их. Например, существует объект DataTransfer , который кроме всего прочего хранит информацию о локальных файлах, если они перетаскиваются. В этом туториале нам не понадобится использовать эти возможности, но без DataTransfer не обойтись, если нужно, например, загружать файлы с компьютера и считывать данные, чтобы затем производить с ними какие-либо манипуляции. Подробно об этом на MDN.
Приступим к созданию нашего списка задач и рассмотрим на примере, как работать с HTML Drag and Drop API.
Вёрстка и стилизация списка задач
Список будет состоять из нескольких задач и заголовка. Для начала создадим разметку. Здесь всё просто — если речь идёт о списке, значит нужен тег ul .
Теперь добавим элементам базовую стилизацию:
Здесь стоит обратить внимание на тип курсора, который мы указали — move . Все элементы списка смогут перемещаться, а с помощью курсора move мы подсказываем пользователю, что есть такая возможность.
Также мы задали стилизацию для класса selected , который чуть позже будем добавлять программно при взаимодействии с элементом
Реализация drag & drop
Шаг 1. Разрешим перетаскивание элементов
Переходим к JavaScript. В первую очередь присвоим элементам упомянутый ранее атрибут draggable со значением true , чтобы разрешить задачам перемещаться. Это можно сделать прямо в разметке или с помощью JavaScript.
Уже сейчас перетаскивание доступно для элементов, но пока это выражается только в появлении фантомной копии. Своего положения элементы не меняют, добавим перемещение чуть позже.
Шаг 2. Добавим реакцию на начало и конец перетаскивания
Будем отслеживать события dragstart и dragend на всём списке. В начале перетаскивания будем добавлять класс selected элементу списка, на котором было вызвано событие. После окончания перетаскивания будем удалять этот класс.
Шаг 3. Реализуем логику перетаскивания
Мы добрались до основной части — перетаскивания задач. Будем отслеживать местоположение перемещаемого элемента относительно других, подписавшись на событие dragover . Благодаря тому, что оно срабатывает очень часто, мы сможем на лету вставлять элемент в нужное место в зависимости от положения курсора. Для этого реализуем такую логику:
Делаем всю область списка доступной для сброса.
Находим выбранный элемент .selected и тот элемент, на котором сработало событие dragover .
Проверяем, что событие dragover сработало не на выбранном элементе, потому что иначе перемещать элемент нет смысла — он уже на нужном месте.
Также проверяем, что dragover сработало именно на одном из элементов списка. Это важно, потому что курсор может оказаться и на пустом пространстве между элементами, а оно нас не интересует.
Находим элемент, перед которым нужно осуществить вставку. Сделаем это, сравнив положение выбранного элемента и текущего, на который наведён курсор.
Вставляем выбранный элемент на новое место.
В целом получившийся на этом этапе код — рабочий. Уже сейчас элементы можно сортировать так, как мы и планировали. Но при этом у варианта есть недостаток — перемещаемый элемент меняет положение в тот момент, когда курсор попадает на другой элемент. Такое поведение недостаточно оптимально и стабильно. С точки зрения пользователя логичнее ориентироваться на центр элемента. То есть мы должны осуществлять вставку только после того, как курсор пересечёт центральную ось, а не сразу после наведения на элемент. Чтобы реализовать это поведение, напишем функцию для получения nextElement другим способом.
Шаг 4. Учтём положение курсора относительно центра
Функция должна принимать на вход вертикальную координату курсора и текущий элемент, на котором сработало событие dragover . Мы будем сравнивать текущее положение курсора с центральной осью элемента, над которым курсор находится в момент перетаскивания. Таким образом, если мы хотим поменять элементы местами, то вставка должна сработать в тот момент, когда курсор пересекает центральную ось. Значит нас интересуют вертикальные координаты курсора и центра элемента, над которым он находится.
Давайте создадим функцию getNextElement() . Мы уже знаем, что она должна возвращать тот элемент, перед которым нужно сделать вставку. В этом нам поможет координата курсора и текущий элемент, которые будут переданы в параметры.
Чтобы получить вертикальную координату текущего элемента, используем метод getBoundingClientRect() . Он вызывается на элементе и возвращает объект, в свойствах которого находится информация о размерах и координатах элемента относительно вьюпорта. Нам понадобится координата y , но также нужно будет учесть высоту элемента height , потому что у рассчитывается относительно верхнего левого угла элемента, а нам нужен центр.
Давайте закрепим на примере. Допустим, мы хотим поменять два элемента местами — начинаем перемещать нижний элемент, наводим курсор на элемент перед ним. Пока мы не приблизились к центру элемента, ничего происходить не должно, потому что пока порядок элементов в DOM изменять не нужно. Но как только курсор пересечёт центральную ось, перемещаемый элемент будет вставлен перед тем элементом, на который мы навели курсор.
Всё почти готово, но нам нужно ещё учесть ситуацию, когда во время перемещения курсор был наведён на какой-то элемент и при этом центральную ось так и не пересёк. Для нас это значит, что порядок не изменился, и ничего делать не надо. Но программа пока об этом не знает и в таких ситуациях осуществляет вставку в DOM на то же самое место при каждом срабатывании события dragover . Как мы помним, оно срабатывает очень часто и каждый раз влечёт за собой ненужные операции с DOM. Мы изменим это поведение, добавив проверку.
Теперь всё работает так, как нужно: мы отслеживаем положение курсора относительно центра, лишние операции в DOM исключили и, главное, элементы сортируются — задача выполнена! Полный код решения — в нашей интерактивной демонстрации.
Полезности
HTML Drag and Drop API на MDN. Вся основная информация об API.
Native HTML5 Drag and Drop. Статья с описанием основных возможностей API и примером использования. Есть перевод на русский.
How to make a Drag-and-Drop file uploader with vanilla JavaScript. Статья о том, как реализовать загрузку файлов с помощью drag & drop.
Drag & drop с событиями мыши. Пример, как реализовать эффект без использования HTML Drag and Drop API. Это может понадобиться, например, из-за плохой поддержки API мобильными браузерами.
10 Best Drag And Drop JavaScript Libraries. Список JavaScript-библиотек, с помощью которых можно усовершенствовать встроенный drag & drop.
Придя впервые к технологии DRAG and DROP столкнулся с очень тяжелым её описанием (Это мое субъективное мнение. Прошу с ним не соглашаться, а перечитать все что только можно и посмотреть на этот вопрос с многих сторон). И решил написать пару статей, нацеленных на начинающих разработчиков, кто хочет познать дзен.
Статья будет состоять из двух частей:
- Метод создания DRAG and DROP эффектов.
- Практическое применение полученных знаний для создание сортировки при помощи DRAG and DROP
Параграф №1 Метод создания DRAG and DROP эффекта
Перед началом глубокого разбора, посмотрим поверхностно. Представьте себя в роли грузчика, вам необходимо перемесить коробку с одного места на другое. Для грузчика это «Ну взял, ну перенес. Готово!», а для программиста “Подошел к коробке, наклонился, взял коробку, поднял коробку, прошел N шагов, наклонился, отпустил коробку.”. Я это к тому, что перед началом работы проиграйте все в голове, по шагам и вы станете гораздо ближе к истине.
Я хочу оговориться, что методов создания данного эффекта несколько и обязательно прочтите о всех. Я воспользуюсь тем, который на данный момент считаю самым удобным.
При создании DRAG and DROP первым шагом необходимо объекту, который мы будем перемещать, присвоить значение draggable='true'.
,
На первом этапе я хочу показать сам процесс, а после мы его распространим на все объекты. Мы сейчас работаем в JS и как вам известно, в браузере существуют различные события, к которым мы можем привязать свои последовательности действий. Давайте разберем необходимые события для создания DRAG and DROP:
dragstart — происходит, когда пользователь начинает перетаскивать элемент.
drag — происходит, когда элемент перетаскивается.
dragend — происходит, когда пользователь закончил перетаскивание элемента.
dragenter — происходит, когда перетаскиваемый элемент попадает в целевой объект.
dragleave — происходит, когда перетаскиваемый элемент покидает целевой объект.
dragover — происходит, когда перетаскиваемый элемент находится над целью.
drop — происходит, когда перетаскиваемый элемент падает на целевой объект.
Теперь очень важная информация! События делятся на две группы. Для перемещаемого элемента (тот кого мы перетаскиваем): dragstart, Drag, Dragend. Для принимающего элемента (куда перетаскиваем): Dragenter, Dragleave, Dragover, Drop. И эти события не могут работать наоборот, но они могут работать друг без друга.
К примеру: Необходимо переместить объект и оставить его там, где мы отпустили кнопку мыши. Эта задача не требует принимающей части.
Бесспорно, пример сделан на коленке, но он замечательно иллюстрирует не обязательность принимающего объекта.
Параграф №2. Не работает DROP в DRAG and DROP
Когда вы попробуете все события, вы обнаружите что drop не работает. Это разработчики данного метода делают атата тем, кто решил «И это всё… Хух, ерунда».
Ну тут все просто, перед событием drop необходимо на этот же элемент повесить событие
В этой лекции мы рассмотрим расширенные возможности работы с элементами управления, а также отработаем создание почтовой программы Ballet.
Операция Drag-and-Drop
Операция Drag-and-Drop предназначена для перемещения и копирования объектов при помощи мыши. Выделив фрагмент текста в Microsoft Word, его можно перетащить в другой документ или в другую позицию текущего документа, нажав и затем отпустив левую кнопку мыши (рис. 3.1).
Рис. 3.1. Перетаскивание выделенного фрагмента текста
Создайте новое приложение и назовите его DragAndDrop . Расположите на форме два элемента TextBox и один RichTextBox , установив следующие значения свойств:
Значение True свойства AllowDrop разрешает размещение текстового фрагмента в данном элементе. Выделяем элемент textBox1 , в окне Properties переключаемся на его события и дважды щелкаем в поле MouseDown. В обработчике этого события вызываем метод DoDragDrop :
В качестве второго параметра метода мы передаем одно из значений перечисления DragDropEffects — здесь мы выбрали копирование текста Copy. Итак, при нажатой кнопке мыши содержимое текста первого текстового поля будет скопировано. В окне Properties второго элемента TextBox дважды щелкаем в поле события DragEnter . В обработчике этого события будем проверять соответствие представляемых данных формату String :
И наконец, при отпускании кнопки мыши в поле этого же элемента будет выводиться скопированный текст:
Для реализации операции Drag-and-Drop в списке событий окна Properties элемента RichTextBox нет нужных нам событий DragDrop и DragEnter , поэтому в области Windows Form Designer generated code нам придется объявить обработчиков для этих событий:
Делаем проверку на соответствие формату String :
Элемент RichTextBox может содержать текст, который нам следует не замещать, а добавлять к нему перетаскиваемый фрагмент. Поэтому обработчик события DragDrop будет немного сложнее:
Запускаем приложение. Теперь можно перетаскивать текст из верхнего текстового поля в нижнее, а в элемент RichTextBox — даже фрагмент текста из Microsoft Word (рис. 3.2):
увеличить изображение
Рис. 3.2. Приложение DragAndDrop, перетаскивание фрагмента текста из Microsoft Word
На диске, прилагаемом к книге, вы найдете приложение DragAndDrop (Code\Glava3\ DragAndDrop).
В данном туториале мы рассмотрим встроенный механизм перетаскивания элементов на странице.
Справедливости ради следует отметить, что указанный механизм можно реализовать с помощью событий мыши, как показывает Илья Кантор в своем учебнике, однако мы будем использовать нативные средства, опираясь на спецификацию.
Для стилизации будет использоваться Bootstrap.
Если вам это интересно, прошу следовать за мной.
Разметка:
Здесь у нас имеется контейнер с полем для ввода текста задачи и кнопкой для ее добавления в список (input-group), а также три контейнера-колонки (list-group) для всех задач (todos-list), задач в процессе выполнения (in-progress-list) и завершенных задач (completed-list). Что касается атрибутов «data», то они предназначены для разделения стилизации и управления: классы — для стилизации, data — для управления.
Классы «in-progress» и «completed» служат индикаторами нахождения задачи в соответствующей колонке. Класс «drop» предназначен для визуализации попадания задачи в зону для «бросания».
Прежде чем переходить к скрипту, отметим, что нами будут использоваться далеко не все события перетаскивания, но большинство из основных.
Определяем главный контейнер, в котором будет осуществляться поиск элементов и которому будет делегирована обработка событий:
Реализуем добавление и удаление задач через обработку клика:
Переходим непосредственно к перетаскиванию.
Для начала реализуем попадание в зону для «бросание» и уход из нее посредством добавления/удаления соответствующего класса:
Далее обрабатываем начало перетаскивания:
Теперь нам нужно каким-то образом отслеживать элемент, находящийся под перетаскиваемым. Это необходимо для того, чтобы произвольно располагать задачи в списке, т.е. менять задачи в колонке местами. При обработке события «mousemove» для этого используется метод «elementFromPoint(x, y)». Прелесть рассматриваемого интерфейса состоит в том, что для определения «низлежащего» элемента нам достаточно обработать событие «dragover»:
Наконец, обрабатываем событие «drop» («бросание»):
Вот и все. Как видите, ничего сложного. Зато какие возможности по добавлению интерактивности на страницу. Осталось дождаться, когда мобильные браузеры реализуют данную технологию, и будет всем счастье.
Надеюсь, вы нашли для себя что-нибудь интересное. Благодарю за внимание и хорошего дня.
Читайте также: