Придя впервые к технологии DRAG and DROP столкнулся с очень тяжелым её описанием (Это мое субъективное мнение. Прошу с ним не соглашаться, а перечитать все что только можно и посмотреть на этот вопрос с многих сторон). И решил написать пару статей, нацеленных на начинающих разработчиков, кто хочет познать дзен.

Статья будет состоять из двух частей:

  • Метод создания DRAG and DROP эффектов.
  • Практическое применение полученных знаний для создание сортировки при помощи DRAG and DROP

Параграф №1 Метод создания DRAG and DROP эффекта


Перед началом глубокого разбора, посмотрим поверхностно. Представьте себя в роли грузчика, вам необходимо перемесить коробку с одного места на другое. Для грузчика это «Ну взял, ну перенес. Готово!», а для программиста “Подошел к коробке, наклонился, взял коробку, поднял коробку, прошел N шагов, наклонился, отпустил коробку.”. Я это к тому, что перед началом работы проиграйте все в голове, по шагам и вы станете гораздо ближе к истине.

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

При создании DRAG and DROP первым шагом необходимо объекту, который мы будем перемещать, присвоить значение draggable='true'.

<html>
  <head>
    <style>
      .ddd {
	display:block;
	float:left;
	padding:10px;
	border:1px solid #000;
      }
    </style>
  </head>
  <body>
    <div id='d1' class='ddd' draggable='true'>Pervii 1</div>
  </body>
</html>

,
На первом этапе я хочу показать сам процесс, а после мы его распространим на все объекты. Мы сейчас работаем в JS и как вам известно, в браузере существуют различные события, к которым мы можем привязать свои последовательности действий. Давайте разберем необходимые события для создания DRAG and DROP:

dragstart — происходит, когда пользователь начинает перетаскивать элемент.
drag — происходит, когда элемент перетаскивается.
dragend — происходит, когда пользователь закончил перетаскивание элемента.
dragenter — происходит, когда перетаскиваемый элемент попадает в целевой объект.
dragleave — происходит, когда перетаскиваемый элемент покидает целевой объект.
dragover — происходит, когда перетаскиваемый элемент находится над целью.
drop — происходит, когда перетаскиваемый элемент падает на целевой объект.

Теперь очень важная информация! События делятся на две группы. Для перемещаемого элемента (тот кого мы перетаскиваем): dragstart, Drag, Dragend. Для принимающего элемента (куда перетаскиваем): Dragenter, Dragleave, Dragover, Drop. И эти события не могут работать наоборот, но они могут работать друг без друга.

К примеру: Необходимо переместить объект и оставить его там, где мы отпустили кнопку мыши. Эта задача не требует принимающей части.

<html>
  <head>
    <style>
      .ddd {
  	display:block;
	float:left;
	padding:10px;
	border:1px solid #000;
      }
    </style>
  </head>
  <body>
    <div id='d1' class='ddd' draggable='true'>Pervii 1</div>
    <div id='d2' class='ddd'>Vtoroy 2</div>
    <div id='d3' class='ddd'>Tretii 3</div>
    <div id='d4' class='ddd'>Chetverty 4</div>
    <div id='d5' class='ddd'>Pyatii 5</div>	
    <script>
	var d1 = document.getElementById('d1');
	var startCursorX;
	var startCursorY;
	var startX;
	var startY;
	d1.addEventListener('dragstart',function() {
	  startCursorX = event.pageX;//Начальная позиция курсора по оси X
	  startCursorY = event.pageY;//Начальная позиция курсора по оси Y
	  startX = d1.style.marginLeft.replace('px','')*1; // Нам нужны только цыфры без PX
	  startY = d1.style.marginTop.replace('px','')*1;
	});
	d1.addEventListener('dragend',function() {
	  d1.style.position = 'absolute';//CSS теперь элемент "Блуждающий" :) 
	  d1.style.marginLeft = startX + event.pageX-startCursorX; //позиция элемента + позиция курсора - позиция курсоа в начале перетаскивания
	  d1.style.marginTop = startY + event.pageY-startCursorY; // Так же как и в предыдущем случае, только по другой оси
	});
    </script>
  </body>
</html>

Бесспорно, пример сделан на коленке, но он замечательно иллюстрирует не обязательность принимающего объекта.

Я считаю примеры с отдельными событиями ни к чему, так как если в строчке d1.addEventListener('dragstart',function() { вы замените 'dragstart' на любое другое событие, сами сможете с ним поиграть и получить интересные результаты. Давайте даже так, поиграйте и то что вам показалось необычным и интересным покажите в комментариях, так каждый узнает много нового и сам сможет это повторить.

Параграф №2. Не работает DROP в DRAG and DROP


Когда вы попробуете все события, вы обнаружите что drop не работает. Это разработчики данного метода делают атата тем, кто решил «И это всё… Хух, ерунда».

Ну тут все просто, перед событием drop необходимо на этот же элемент повесить событие

d2.addEventListener('dragover',function() {
  event.preventDefault();
});

Это примите как факт, без него работать не будет.

Комментарии (5)


  1. BigDflz
    13.08.2019 13:00

    есть вопрос по изображению объекта во время перемещения. Если объект не большой (примерно до 250*250 px) то он отображается нормально, но если объект больше то на него накладывается радиальная прозрачность с центром в точке захвата и к границам становится совсем прозрачной, что очень не удобно. Есть ли возможность изменить вид прозрачности? В мануалах я не нашёл упоминание и прозрачности, плохо искал?


    1. GeneAYak
      14.08.2019 11:22

      Сам не проовал, но вообще есть такой раздел в документации
      Возможно, это то, что нужно


      1. BigDflz
        14.08.2019 16:41

        спасибо, при следующем применении проверю.


  1. ua30
    14.08.2019 10:11

    Весьма интересно, не знал про значение атрибут draggable.

    Подобные статьи гораздо лучше воспринимаются с живым demo. Дублируйте код в https://jsfiddle.net/ и давайте линк.

    Пример кода простой, но называть класс именем «ddd» не есть хорошо. Порядок важен и в мелочах.

    Неплохо было бы так же упомянуть о неплохой, но не полной поддержке технологии на данный момент https://caniuse.com/#search=draggable — 84.35% + 3.5% частично. ИМХО, пока и удобней и практичней пользоваться соответствующими JS библиотеками.


  1. Minu
    14.08.2019 12:38

    В примере после d1.style.marginLeft =… и d1.style.marginTop =… не хватает + 'px'.