javascripthtmldrag-and-dropdom-eventsevent-capturing

OnDrop Event Target is children when dropped over children, even when Capture phase is used


I'm trying to make my Drag and Drop work properly in JavaScript without having to explicitly scan for the parent element nor other ID, class loops and other hacky magic.

Right now when I drag one of the elements in the element pool and drag it to the grey zone, the element is copied to the drop zone (So far everything is correct). I basically copy the HTML of the dragged element, clear the drop zone (target) and add the new element to it.

The problem starts when you have already some child element in the drop zone and you drag it over the child element. Then the target in the drop event is the child element and not the div where the event has been attached to. This results in the dragged element clearing and copying itself into the children instead of the drop container.

The expected result should be that no matter if you drop it over the children or on the main drop container directly, the target should always be the parent node as I am using Event Capture mode.

Here is my example code in JS Fiddle. I'm using a event listener with capture, but it seems to be doing the bubbling phase anyway and ignoring the capture one. Why?

What is the proper way to do this?

function onDrag(ev) {
  var el = ev.target;
  //console.log("Drag: " + ev.target.outerHTML)
  ev.dataTransfer.setData('text/html', ev.target.outerHTML);
}

function onContainerOver(ev) {
  ev.preventDefault();
  ev.dataTransfer.dropEffect = "move"
}

function onContainerDrop(ev) {
  ev.preventDefault();
  //console.log("Drop: " + ev.dataTransfer.getData("text/html"))
  ev.target.innerHTML = ev.dataTransfer.getData("text/html");
}

document.getElementById('target').addEventListener('drop', onContainerDrop, true); // This should not bubble, but it does. Target is always the child first rather than the element where the event listener resides. Why???
#list {}

.el {
  padding: 5px;
  margin: 10px;
  border: solid 1px black;
  background-color: #86d4ff;
  cursor: move;
    line-height: 1em;
    text-align: left;
}

#target {
  min-height: 200px;
  width: 100%;
  background-color: #ffdea6;
  border: 1px dashed gray;
  text-align: center;
  line-height: 100px;
}
<div id="list">
  <div class="el" draggable="true" ondragstart="onDrag(event)">ELEMENT A</div>
  <div class="el" draggable="true" ondragstart="onDrag(event)">ELEMENT B</div>
  <div class="el" draggable="true" ondragstart="onDrag(event)">ELEMENT C</div>
</div>
<div id="target" ondragover="onContainerOver(event)">
  Drop Here
</div>

The Original JSFiddle Link.


Solution

  • Never mind, I didn't notice the availability of the "currentTarget" element in the Event which does exactly what I was looking for.

    So instead of using "event.target" it should be "event.currentTarget" to get the real parent element where the event is triggered.

    Here is a working JSFiddle: https://jsfiddle.net/xs20xy27/

    Updated JS:

    function onDrag(ev){
      var el = ev.srcElement;
      console.log("Drag: " + ev.srcElement.outerHTML)
      ev.dataTransfer.setData('text/html', ev.srcElement.outerHTML);
    }
    
    function onContainerOver(ev){
      ev.preventDefault();
      ev.dataTransfer.dropEffect = "move"
    }
    
    function onContainerDrop(ev){
      ev.preventDefault();
      console.log("Drop: " + ev.dataTransfer.getData("text/html"))
      ev.currentTarget.innerHTML =   ev.dataTransfer.getData("text/html");
    }
    
    document.getElementById('target').addEventListener('drop', onContainerDrop, true); // This should not bubble, but it does. Target is always the child first rather than the element where the event listener resides. Why???