为什么dragleave事件会意外触发?

7
请查看演示 - http://jsfiddle.net/fSB32/2/ 我的意图是当拖动事件在文档上触发时,出现一个遮罩层。因此,用户可以将图像拖放到文档的任何位置。当他这样做时,会出现一个酷炫的遮罩层。在拖动事件完成时,遮罩层应该隐藏自己。
我面临的问题是,dragleave事件意外地被触发(请参见演示中的控制台),导致遮罩层一出现就隐藏。如果您注释掉隐藏遮罩层的代码,则它自然会显示出来并永远不会消失。

2
如果人们解释一下为什么他们投了反对票或者关闭了问题,那将是有建设性的。这样我下次就可以提出更好的问题了。 - AppleGrew
2个回答

21

我自己解决了这个问题。

原因

拖动事件会向上传递。所以,我认为由于这个原因,当我使我的叠加层可见时,它会触发该事件dragenter,并且可能在进一步拖动时,它的子项会触发dragleave,并且冒泡到我正在监听的父级。

然而,事实证明,无论dragenter的目标是孩子还是其他,dragleave都会在触发dragenter时触发!所以,在我的情况下,一旦我使叠加层可见,就会在其上触发dragenter,在documentwindow上触发dragleave

修复方法

因此,理想的放置目标不能有任何子元素,这样在自身内部拖动不会触发dragleave。在我的情况下,放置目标叠加层占据了整个窗口空间,因此仅需在叠加层DOM上监听dragleave即可解决90%的问题。

但是,当用户拖动其中的子元素div时,仍然存在问题。 解决方案也很简单。我的要求是将子div对鼠标事件隐藏。为此,我们有神奇的CSS-pointer-events。将此属性设置为none即可解决问题。唯一的缺点是,在IE 11之前不支持此功能。

查看演示http://jsfiddle.net/fSB32/5/

如果你确实需要支持IE11以下版本,那么可以使用一个技巧,在拖放目标中放置一个空的div作为子元素,并确保它具有最高的z-index并覆盖整个拖放目标。这样,拖动事件应该只会被定向到该DOM,因为所有其他子元素都"穿过"它可见。


1
这里是一个没有子元素限制的“双击”解决方案。正如最佳答案所说,与“mouseover”不同,“dragover”和“dragleave”事件不将子元素作为整体考虑,因此每次鼠标经过任何子元素时,“dragleave”都会被触发。考虑到文件上传,我创建了一个小部件,允许:1.使用$ _FILES拖放桌面文件;2.使用$ _POST和cURL将图像/元素或URL拖放到浏览器中;3.使用按钮附加设备文件,使用$ _FILES;4.使用输入框编写/粘贴URL图像/元素,使用$ _POST和cURL。

enter image description here

问题:由于所有内容,包括表单输入和图像,都在DIV子元素中,即使未离开虚线,也会触发“dragleave”事件。使用属性“pointer-events:none”不是一种选择,因为方法3和4需要触发“onchange”事件。
解决方案?当鼠标进入时,覆盖所有放置容器的重叠DIV,并且唯一具有“pointer-events:none”的子元素。
结构:
- div #drop-container:主div,保持所有内容在一起 - div #drop-area: “dragenter”侦听器和立即触发#drop-pupup - div #drop-pupup:与#drop-area相同级别,“dragenter”,“dragleave”和“drop”侦听器
然后,当鼠标通过将元素拖到#drop-area中进入时,立即显示#drop-pupup并连续的事件发生在此div上而不是初始接收器上。

enter image description here

以下是JS/jQuery代码。我已经留下PoC,所以不要浪费我失去的所有时间。

jQuery(document).on('dragover', '#drop-area', function(event) {
 event.preventDefault();
 event.stopPropagation();
 jQuery('#drop-popup').css('display','block');
});

jQuery(document).on('dragover dragleave drop', '#drop-popup', function(event) {
 event.preventDefault();
 event.stopPropagation();

 console.log(event.type);

 // layout and drop events
 if ( event.type == 'dragover') {
  jQuery('#drop-popup').css('display','block');
 }
 else {
  jQuery('#drop-popup').css('display','none');
 
  if ( event.type == 'drop' ) {
   // do what you want to do
   // for files: use event.originalEvent.dataTransfer.files
   // for web dragged elements: use event.originalEvent.dataTransfer.getData('Text') and CURL to capture
  }
 }
});
body {
  background: #ffffff;
  margin: 0px;
  font-family: sans-serif;
}

#drop-container {
  margin: 100px 10%; /* for online testing purposes only */
  width: 80%; /* for jsfiddle purposes only */
  display: block;
  float: left;
  overflow: hidden;
  box-sizing: content-box;
  position: relative; /* needed to use absolute on #drop-popup */
  border-radius: 5px;
  text-align: center;
  cursor: default;
  border: 2px dashed #000000;
}

#drop-area {
  display: block;
  float: left;
  padding: 10px;
  width: 100%;
}

#drop-popup {
  display: none;
  box-sizing: content-box;
  position: absolute;
  width: 100%;
  top: 0;
  left: 0;
  background: linear-gradient(to BOTTOM, rgba(245, 245, 245, 1) , rgba(245, 245, 245, 0));
  height: 512px;
  padding: 20px;
  z-index: 20;
}

#drop-popup > p {
   pointer-events: none;
}
<html>
  <head>
    <title>Drag and Drop</title>
  </head>
  <body>

    <div id="drop-container">
      <div id="drop-area">
        <p>Child paragraph content inside drop area saying "drop a file or an image in the dashed area"</p>
        <div>This is a child div No. 1</div>
        <div>This is a child div No. 2</div>
      </div>
      <div id="drop-popup">
        <p>This DIV will cover all childs on main DIV dropover event and current P tag is the only one with CSS "pointer-events: none;"</p>
      </div>
    </div>
    
    <script src="https://code.jquery.com/jquery-3.4.1.min.js" type="text/javascript"></script>
  </body>
<html>

关于jQuery的"on"方法,使用它时在div id内部触发事件,这样你就可以开始隐藏"上传框"的事件触发。
最后,我更喜欢使用"dragover"而不是"dragenter",因为它有一个小延迟(毫秒级),有利于性能(https://developer.mozilla.org/en-US/docs/Web/API/Document/dragover_event)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接