即使使用捕获阶段,OnDrop事件目标也是在儿童身上丢弃的子项

时间:2017-12-14 10:59:16

标签: javascript html drag-and-drop dom-events event-capturing

我正在尝试使我的拖放工作在JavaScript中正常工作,而不必显式扫描父元素或其他ID,类循环和其他hacky魔法。

现在,当我拖动元素池中的一个元素并将其拖动到灰色区域时,该元素将被复制到放置区域(到目前为止一切正确)。我基本上复制拖动元素的HTML,清除放置区域(目标)并向其添加新元素。

当您在放置区中已经有一些子元素并将其拖到子元素上时,问题就开始了。然后drop事件中的目标是子元素,而不是附加事件的div。这会导致拖动的元素清除并将其自身复制到子项而不是drop容器中。

预期的结果应该是无论是将它放在子节点上还是直接放在主放置容器上,目标都应该是父节点,因为我正在使用事件捕获模式。

这是我在JS Fiddle中的示例代码。我正在使用带捕获的事件监听器,但它似乎正在进行冒泡阶段而忽略了捕获阶段。为什么呢?

这样做的正确方法是什么?

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>

原件JSFiddle Link.

2 个答案:

答案 0 :(得分:6)

当您在子元素上放置任何内容或者在子元素上发生任何事件时,事件目标始终是子元素,因为这是事件发生的位置。这是目标。

┌─────────────────────────────────────┐
│   ┌────────────────────────┐        │
│   │     Child [Target]     │        │
│   └────────────────────────┘        │
│             Parent                  │
└─────────────────────────────────────┘

然后将此事件作为子元素传递给子元素的事件处理程序,作为目标 CurrentTarget

┌─────────────────────────────────────┐
│   ┌───────────────────────────────┐ │
│   │ Child [Target][CurrentTarget] │ │
│   └───────────────────────────────┘ │
│         Parent Element              │
└─────────────────────────────────────┘

然后将此事件作为子元素传递给父元素的事件处理程序,作为目标,将父元素传递为 CurrentTarget

┌─────────────────────────────────────┐
│   ┌───────────────────────────────┐ │
│   │        Child [Target]         │ │
│   └───────────────────────────────┘ │
│         Parent [CurrentTarget]      │
└─────────────────────────────────────┘

然后以相同的方式将事件连续传递给dom树中更高的元素,直到达到顶级元素或任何事件处理程序通过调用 event.stopPropagation取消此事件冒泡( )

那么当你创建事件处理程序时会发生什么,将第三个参数 useCapture 传递为 true

parentElement.addEventListener('click', myClickHandler, true);

在这种情况下,事件首先传递给父元素[ currentTarget:Parent ],然后传递给子元素[ currentTarget:Child ]但是一如既往, 事件目标始终是子元素,因为这是触发事件的位置。

关于 The CurrentTarget this MDN WebDocs page说,

  

在事件遍历时标识事件的当前目标   DOM。它总是指事件处理程序所在的元素   已被附加,而不是event.target标识   事件发生的元素。

答案 1 :(得分:1)

没关系,我没有注意到&#34; currentTarget &#34; 事件中的元素完全符合我的要求。

所以不要使用&#34; event.target &#34;它应该是&#34; event.currentTarget &#34;获取触发事件的真实父元素。

这是一个有效的JSFiddle: https://jsfiddle.net/xs20xy27/

更新了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???