Drag and Drop через события мыши

Почитал статью про реализацию Drag and Drop через события мыши (https://learn.javascript.ru/drag-and-drop-objects), Скопировал код js, но столкнулся с трудностью. Не срабатывает display:none при mouseup над droppable элементом. Поигравшись с консолью понял, что скорее всего дело в этих строчках (они идут не подряд, я их выбрал отдельно):

document.body.appendChild(avatar);

var elem = document.elementFromPoint(event.clientX, event.clientY);

return elem.closest('.droppable');

Происходит здесь следующее:

  • аватар добавляется в body, после чего находится самый вложеный элемент под мышью (с помощью консоли узнал, что он выдаёт draggable элемент)

  • далее с помощью closest ищется .droppable над которым сейчас мышка, но здесь всегда возвращается null (скорее всего из-за структуры HTML)

Если я вложу draggable не в body, а в #tour_5, то droppable ищется правильно, но тогда некорректо отображается перемещение draggable элемента из-за строчек, реализующих перемещение:

top: box.top + pageYOffset,
left: box.left + pageXOffset

Как я понял, это происходит потому что relative выступает #tour5 а position расчитывается относительно всей страницы.

Также если я уберу у #tour_5 relative, то droppable перестанет находиться (выдаёт null)

Я вижу здесь 2 варианта решения:

  • Реализовать перемещение не относительно страницы, а относительно #tour_5
  • реализовать правильный выбор droppable элемента даже когда перемещаю draggable в body

Помогите разобраться как это корректо реализовать, пожалуйста

var DragManager = new function() {

  /**
   * составной объект для хранения информации о переносе:
   * {
   *   elem - элемент, на котором была зажата мышь
   *   avatar - аватар
   *   downX/downY - координаты, на которых был mousedown
   *   shiftX/shiftY - относительный сдвиг курсора от угла элемента
   * }
   */
  var dragObject = {};

  var self = this;

  function onMouseDown(e) {

    if (e.which != 1) return;

    var elem = e.target.closest('.draggable');
    if (!elem) return;

    dragObject.elem = elem;

    // запомним, что элемент нажат на текущих координатах pageX/pageY
    dragObject.downX = e.pageX;
    dragObject.downY = e.pageY;

    return false;
  }

  function onMouseMove(e) {
    if (!dragObject.elem) return; // элемент не зажат

    if (!dragObject.avatar) { // если перенос не начат...
      var moveX = e.pageX - dragObject.downX;
      var moveY = e.pageY - dragObject.downY;

      // если мышь передвинулась в нажатом состоянии недостаточно далеко
      if (Math.abs(moveX) < 3 && Math.abs(moveY) < 3) {
        return;
      }

      // начинаем перенос
      dragObject.avatar = createAvatar(e); // создать аватар
      if (!dragObject.avatar) { // отмена переноса, нельзя "захватить" за эту часть элемента
        dragObject = {};
        return;
      }

      // аватар создан успешно
      // создать вспомогательные свойства shiftX/shiftY
      var coords = getCoords(dragObject.avatar);
      dragObject.shiftX = dragObject.downX - coords.left;
      dragObject.shiftY = dragObject.downY - coords.top;

      startDrag(e); // отобразить начало переноса
    }

    // отобразить перенос объекта при каждом движении мыши
    dragObject.avatar.style.left = e.pageX - dragObject.shiftX + 'px';
    dragObject.avatar.style.top = e.pageY - dragObject.shiftY + 'px';

    return false;
  }

  function onMouseUp(e) {
    if (dragObject.avatar) { // если перенос идет
      finishDrag(e);
    }

    // перенос либо не начинался, либо завершился
    // в любом случае очистим "состояние переноса" dragObject
    dragObject = {};
  }

  function finishDrag(e) {
    var dropElem = findDroppable(e);

    if (!dropElem) {
      self.onDragCancel(dragObject);
    } else {
      self.onDragEnd(dragObject, dropElem);
    }
  }

  function createAvatar(e) {

    // запомнить старые свойства, чтобы вернуться к ним при отмене переноса
    var avatar = dragObject.elem;
    var old = {
      parent: avatar.parentNode,
      nextSibling: avatar.nextSibling,
      position: avatar.position || '',
      left: avatar.left || '',
      top: avatar.top || '',
      zIndex: avatar.zIndex || ''
    };

    // функция для отмены переноса
    avatar.rollback = function() {
      old.parent.insertBefore(avatar, old.nextSibling);
      avatar.style.position = old.position;
      avatar.style.left = old.left;
      avatar.style.top = old.top;
      avatar.style.zIndex = old.zIndex
    };

    return avatar;
  }

  function startDrag(e) {
    var avatar = dragObject.avatar;

    // инициировать начало переноса
    document.getElementById('tour_5').appendChild(avatar);
    avatar.style.zIndex = 9999;
    avatar.style.position = 'absolute';
  }

  function findDroppable(event) {
    // спрячем переносимый элемент
    dragObject.avatar.hidden = true;

    // получить самый вложенный элемент под курсором мыши
    var elem = document.elementFromPoint(event.clientX, event.clientY);

    // показать переносимый элемент обратно
    dragObject.avatar.hidden = false;

    if (elem == null) {
      // такое возможно, если курсор мыши "вылетел" за границу окна
      return null;
    }
      
      console.log(elem);
    return elem.closest('.droppable');
  }

  document.onmousemove = onMouseMove;
  document.onmouseup = onMouseUp;
  document.onmousedown = onMouseDown;

  this.onDragEnd = function(dragObject, dropElem) {};
  this.onDragCancel = function(dragObject) {};

};
function getCoords(elem) { // кроме IE8-
  var box = elem.getBoundingClientRect();

  return {
    top: box.top + pageYOffset,
    left: box.left + pageXOffset
  };
 <div class="tour" id="tour_5">
               <div class="blocks_t5">
                   <div class="block_t5">
                   <span class="block_t5_text">Гора, высшая  точка  хребта Герцена  и   Краснодарского    края.   Высота   3 345,9 м.</span>
                   <div class="ans_t5 droppable"></div>
                   </div>
                   <div class="block_t5">
                   <span class="block_t5_text">Озеро расположено в 8 км от Темрюка. Вода в нем<br>соленая, а знаменито оно запасами лечебной грязи.</span>
                   <div class="ans_t5 droppable"></div>
                   </div>
                   <div class="block_t5">
                   <span class="block_t5_text">Гора, высшая  точка  хребта Герцена  и   Краснодарского    края.   Высота   3 345,9 м.</span>
                   <div class="ans_t5 droppable"></div>
                   </div>
                   <div class="block_t5">
                   <span class="block_t5_text">Озеро расположено в 8 км от Темрюка. Вода в нем<br>соленая, а знаменито оно запасами лечебной грязи.</span>
                   <div class="ans_t5 droppable"></div>
                   </div>
                   <div class="block_t5">
                   <span class="block_t5_text">Гора, высшая  точка  хребта Герцена  и   Краснодарского    края.   Высота   3 345,9 м.</span>
                   <div class="ans_t5 droppable"></div>
                   </div>
                   <div class="block_t5">
                   <span class="block_t5_text">Озеро расположено в 8 км от Темрюка. Вода в нем<br>соленая, а знаменито оно запасами лечебной грязи.</span>
                   <div class="ans_t5 droppable"></div>
                   </div>
                   <div class="block_t5">
                   <span class="block_t5_text">Гора, высшая  точка  хребта Герцена  и   Краснодарского    края.   Высота   3 345,9 м.</span>
                   <div class="ans_t5 droppable"></div>
                   </div>
                   <div class="block_t5">
                   <span class="block_t5_text">Озеро расположено в 8 км от Темрюка. Вода в нем<br>соленая, а знаменито оно запасами лечебной грязи.</span>
                   <div class="ans_t5 droppable"></div>
                   </div>
               </div>
               <div class="static_t5">
                   <span class="var_t5 draggable" id="var_t5_1">В Черном море</span>
                   <span class="var_t5 draggable" id="var_t5_2">Всадник</span>
                   <span class="var_t5 draggable" id="var_t5_3"></span>
                   <span class="var_t5 draggable" id="var_t5_4"></span>
                   <span class="var_t5 draggable" id="var_t5_5"></span>
                   <span class="var_t5 draggable" id="var_t5_6"></span>
                   <span class="var_t5 draggable" id="var_t5_7"></span>
                   <span class="var_t5 draggable" id="var_t5_8"></span>
               </div>
               
                <button class="next_tour" onclick="go_to_tour_6()">
                    <h5>далее</h5>
                </button>
            </div>


Ответы (0 шт):