父元素火灾“dragleave”拖以上的儿童元素时父元素火灾“dragleave”拖以上的儿童元素时

2019-05-17 00:05发布

概观

我有以下HTML结构和我已附加了dragenterdragleave事件到<div id="dropzone">元素。

<div id="dropzone">
    <div id="dropzone-content">
        <div id="drag-n-drop">
            <div class="text">this is some text</div>
            <div class="text">this is a container with text and images</div>
        </div>
    </div>
</div>

问题

当我拖动文件在<div id="dropzone">中, dragenter按预期事件被触发。 然而,当我移动鼠标的子元素,诸如<div id="drag-n-drop">中, dragenter事件被激发为<div id="drag-n-drop">元素,然后将dragleave事件被触发为<div id="dropzone">元素。

如果我将鼠标悬停在<div id="dropzone">元素再次,该dragenter事件再次发射,这是很酷,但随后的dragleave事件被触发的子元素刚离开,所以removeClass指令被执行,这是不冷静。

此行为是有2个原因问题:

  1. 我只是附加dragenterdragleave<div id="dropzone">所以我不明白为什么孩子元素有这些活动连接为好。

  2. 我还拖着在<div id="dropzone">元素,而徘徊在它的孩子,所以我不想dragleave火!

的jsfiddle

这里有一个的jsfiddle鼓捣: http://jsfiddle.net/yYF3S/2/

所以我...怎样才能使它这样,当我拖着在文件<div id="dropzone">元素, dragleave甚至不火,如果我拖过任何儿童的元素...它应该只有火的时候我离开<div id="dropzone">元素......徘徊/拖动周围的元素的边界内的任何地方应不会触发dragleave事件。

我需要这是跨浏览器兼容,在支持HTML5拖放正下降的浏览器至少,所以这个答案是不够的。

这似乎是谷歌和Dropbox已经想通了这一点,但他们的源代码已经过压缩/复杂,所以我一直无法从他们实施摸不着头脑。

Answer 1:

如果你不需要将事件绑定到子元素,你可以随时使用指针的事件属性。

.child-elements {
  pointer-events: none;
}


Answer 2:

我终于找到了一个解决方案,我很高兴。 我居然发现几种方法可以做我想做的,但没有一个是为当前的解决方案是成功的......在一个解决方案,我经历了频繁闪烁的添加/删除边框到的结果#dropzone在另一个元素......,如果您将鼠标悬停在浏览器远离边界从未删除。

不管怎样,我最好的解决办法哈克是这样的:

var dragging = 0;

attachEvent(window, 'dragenter', function(event) {

    dragging++;
    $(dropzone).addClass('drag-n-drop-hover');

    event.stopPropagation();
    event.preventDefault();
    return false;
});

attachEvent(window, 'dragover', function(event) {

    $(dropzone).addClass('drag-n-drop-hover');

    event.stopPropagation();
    event.preventDefault();
    return false;
});

attachEvent(window, 'dragleave', function(event) {

    dragging--;
    if (dragging === 0) {
        $(dropzone).removeClass('drag-n-drop-hover');
    }

    event.stopPropagation();
    event.preventDefault();
    return false;
});

这工作得很好,但问题在Firefox中来,因为火狐是双调用dragenter所以我的计数器是关闭的。 但尽管如此,它不是一个很优雅的解决方案。

然后,我偶然发现了这个问题: 拖动窗口外面时如何检测在Firefox DragLeave事件

所以我把答案 ,并将其应用到我的情况:

$.fn.dndhover = function(options) {

    return this.each(function() {

        var self = $(this);
        var collection = $();

        self.on('dragenter', function(event) {
            if (collection.size() === 0) {
                self.trigger('dndHoverStart');
            }
            collection = collection.add(event.target);
        });

        self.on('dragleave', function(event) {
            /*
             * Firefox 3.6 fires the dragleave event on the previous element
             * before firing dragenter on the next one so we introduce a delay
             */
            setTimeout(function() {
                collection = collection.not(event.target);
                if (collection.size() === 0) {
                    self.trigger('dndHoverEnd');
                }
            }, 1);
        });
    });
};

$('#dropzone').dndhover().on({
    'dndHoverStart': function(event) {

        $('#dropzone').addClass('drag-n-drop-hover');

        event.stopPropagation();
        event.preventDefault();
        return false;
    },
    'dndHoverEnd': function(event) {

        $('#dropzone').removeClass('drag-n-drop-hover');

        event.stopPropagation();
        event.preventDefault();
        return false;
    }
});

这是干净的,优雅的,似乎在每一个我到目前为止测试的浏览器工作(没有测试IE还)。



Answer 3:

这是一个有点难看,但它的工作原理该死!...

在您“的dragenter”处理程序存储event.target(在一个变量您关闭,或任何内),然后在“dragleave”处理程序只解雇你的代码,如果event.target ===一个你保存。

如果您“的dragenter”被射击,当你不希望它(即当它离开子元素之后输入),那么它触发最后一次鼠标离开母体之前,它是在父母,所以父母总是会预期“dragleave”前的最后“的dragenter”。

(function () {

    var droppable = $('#droppable'),
        lastenter;

    droppable.on("dragenter", function (event) {
        lastenter = event.target;
        droppable.addClass("drag-over");            
    });

    droppable.on("dragleave", function (event) {
        if (lastenter === event.target) {
            droppable.removeClass("drag-over");
        }
    });

}());


Answer 4:

起初,我同意人们丢弃的pointer-events: none办法。 但后来我问自己:

你真的需要指针事件的子元素工作的同时拖动过程中

就我而言,我有很多东西要在儿童,如悬停显示额外的动作,内联编辑等按钮......但是, 没有那是必要的或事实上在拖动过程甚至期望。

就我而言,我使用像这样把鼠标事件关闭选择性父容器的所有子节点:

  div.drag-target-parent-container.dragging-in-progress * {
    pointer-events: none;
  }

使用您喜欢的方法来添加/删除类dragging-in-progressdragEnter / dragLeave事件处理程序,因为我做了或做相同dragStart等。 人。



Answer 5:

这似乎是Chrome错误。

我能想到的唯一解决方法是建立一个透明的覆盖元素捕捉到你的活动: http://jsfiddle.net/yYF3S/10/

JS:

$(document).ready(function() {
    var dropzone = $('#overlay');

    dropzone.on('dragenter', function(event) {
        $('#dropzone-highlight').addClass('dnd-hover');
    });

    dropzone.on('dragleave', function(event) {
        $('#dropzone-highlight').removeClass('dnd-hover');
    });

});​

HTML:

<div id="dropzone-highlight">
    <div id="overlay"></div>

    <div id="dropzone" class="zone">
        <div id="drag-n-drop">
            <div class="text1">this is some text</div>
            <div class="text2">this is a container with text and images</div>
        </div>
    </div>
</div>

<h2 draggable="true">Drag me</h2>
​


Answer 6:

问题是,该dropzones内的元素是悬浮窗当然一部分,当你进入孩子,你离开父母。 解决这并不容易。 您可以尝试添加事件,孩子们也再次增加你的类的父。

$("#dropzone,#dropzone *").on('dragenter', function(event) {

    // add a class to #dropzone

    event.stopPropagation(); // might not be necessary
    event.preventDefault();
    return false;
});

你的事件仍然会触发多次,但没有人会看到。

//编辑:使用dragmove事件永久覆盖DragLeave事件:

$("#dropzone,#dropzone *").on('dragenter dragover', function(event) {

    // add a class to #dropzone

    event.stopPropagation(); // might not be necessary
    event.preventDefault();
    return false;
});

只为悬浮窗定义DragLeave事件。



Answer 7:

如果你正在使用jQuery,检查了这一点: https://github.com/dancork/jquery.event.dragout

这是真正真棒。

特殊事件创建处理真正dragleave功能。

HTML5 DragLeave事件更象鼠标移开。 这个插件是为了复制鼠标离开样式功能,同时拖动。

使用例:

$( '#myelement')上( '带出',函数(事件){//你的代码});

编辑:其实,我不认为这是依赖于jQuery的,你可能只需要使用的代码,即使没有它。



Answer 8:

正如benr中提到的这个答案 ,就可以防止子节点开枪事件,但如果你需要绑定一些事件,这样做:

#dropzone.dragover *{
   pointer-events: none;
}

而这一次添加到您的JS代码:

$("#dropzone").on("dragover", function (event) {
   $("#dropzone").addClass("dragover");
});

$("#dropzone").on("dragleave", function (event) {
   $("#dropzone").removeClass("dragover");
});


Answer 9:

@hristo我有一个更优雅的解决方案。 检查,如果这是你可以使用。

你的努力没有白费毕竟。 我设法在第一次使用你的,但不得不在FF,Chrome浏览器不同的问题。 花了这么多小时后,我得到的建议正好发挥预期。

下面是如何实施的。 我也参加了视觉线索的优势,正确引导用户对放置区。

$(document).on('dragstart dragenter dragover', function(event) {    
    // Only file drag-n-drops allowed, http://jsfiddle.net/guYWx/16/
    if ($.inArray('Files', event.originalEvent.dataTransfer.types) > -1) {
        // Needed to allow effectAllowed, dropEffect to take effect
        event.stopPropagation();
        // Needed to allow effectAllowed, dropEffect to take effect
        event.preventDefault();

        $('.dropzone').addClass('dropzone-hilight').show();     // Hilight the drop zone
        dropZoneVisible= true;

        // http://www.html5rocks.com/en/tutorials/dnd/basics/
        // http://api.jquery.com/category/events/event-object/
        event.originalEvent.dataTransfer.effectAllowed= 'none';
        event.originalEvent.dataTransfer.dropEffect= 'none';

         // .dropzone .message
        if($(event.target).hasClass('dropzone') || $(event.target).hasClass('message')) {
            event.originalEvent.dataTransfer.effectAllowed= 'copyMove';
            event.originalEvent.dataTransfer.dropEffect= 'move';
        } 
    }
}).on('drop dragleave dragend', function (event) {  
    dropZoneVisible= false;

    clearTimeout(dropZoneTimer);
    dropZoneTimer= setTimeout( function(){
        if( !dropZoneVisible ) {
            $('.dropzone').hide().removeClass('dropzone-hilight'); 
        }
    }, dropZoneHideDelay); // dropZoneHideDelay= 70, but anything above 50 is better
});


Answer 10:

我的两分钱:隐藏在你的悬浮窗一层则显示它时,你的dragenter和目标就可以了dragleave。

演示: https://jsfiddle.net/t6q4shat/

HTML

<div class="drop-zone">
  <h2 class="drop-here">Drop here</h2>
  <h2 class="drop-now">Drop now!</h2>
  <p>Or <a href="#">browse a file</a></p>
  <div class="drop-layer"></div>
</div>

CSS

.drop-zone{
  padding:50px;
  border:2px dashed #999;
  text-align:center;
  position:relative;
}
.drop-layer{
  display:none;
  position:absolute;
  top:0;
  left:0;
  bottom:0;
  right:0;
  z-index:5;
}
.drop-now{
  display:none;
}

JS

$('.drop-zone').on('dragenter', function(e){
    $('.drop-here').css('display','none');
    $('.drop-now').css('display','block');
    $(this).find('.drop-layer').css('display','block');
    return false;
});

$('.drop-layer').on('dragleave', function(e){
    $('.drop-here').css('display','block');
    $('.drop-now').css('display','none');
    $(this).css('display','none');
    return false;
});


Answer 11:

所以我的方法pointer-events: none; 没有工作也很好......因此,这里是我的另一种解决方案:

    #dropzone {
        position: relative;
    }

    #dropzone(.active)::after {
        position: absolute;
        top: 0;
        right: 0;
        bottom: 0;
        left: 0;
        content: '';
    }

这样,它不可能dragleave父(对孩子)或dragover一个子元素。 希望这可以帮助 :)

*在” .active'级的我,当我添加dragenterdragleave 。 但如果你没有这种工作刚刚离开的类了。



Answer 12:

超级简单快速解决这一点,没有广泛的测试,但目前已经在Chrome工作。

打扰了CoffeeScript的。

  dragEndTimer = no

  document.addEventListener 'dragover', (event) ->
    clearTimeout dragEndTimer
    $('body').addClass 'dragging'
    event.preventDefault()
    return no

  document.addEventListener 'dragenter', (event) ->
    $('section').scrollTop(0)
    clearTimeout dragEndTimer
    $('body').addClass 'dragging'

  document.addEventListener 'dragleave', (event) ->
    dragEndTimer = setTimeout ->
      $('body').removeClass 'dragging'
    , 50

这修复了Chrome的闪烁的bug,或者至少它的排列是造成我的问题。



Answer 13:

我的版本:

$(".dropzone").bind("dragover", function(e){
    console.log('dragover');
});

$(".dropzone").bind("dragleave", function(e) {
  var stopDrag = false;
  if (!e.relatedTarget) stopDrag = true;
  else {
    var parentDrop = $(e.relatedTarget).parents('.dropzone');
    if (e.relatedTarget != this && !parentDrop.length) stopDrag = true;
  }

  if (stopDrag) {
    console.log('dragleave');
  }
});

有了这个布局:

<div class="dropzone">
  <div class="inner-zone">Inner-zone</div>
</div>

我做了元素类的转储e.targete.currentTargete.relatedTarget两者dragoverdragleave事件。

这表明我是在离开母块( .dropzonee.relatedTarget这不就是块,所以我知道我出了悬浮窗的一个孩子。



Answer 14:

在这里,最简单的解决方案●之一︿●

看看这个小提琴 < -尝试拖动某些文件箱内

你可以这样做:

var dropZone= document.getElementById('box');
var dropMask = document.getElementById('drop-mask');


dropZone.addEventListener('dragover', drag_over, false);
dropMask.addEventListener('dragleave', drag_leave, false);
dropMask.addEventListener('drop', drag_drop, false);

由你应该已经知道什么是这里发生的一切。
只要看看小提琴,你知道的。



Answer 15:

我也有过类似的问题,我固定它是这样的:

问题:当用户滴在“放置区域”的元素(ul元素)的功能下降(EV)被激发,但不幸的是,还可以当元素在其子(li元素)的一个下降。

该修补程序:

function drop(ev) { 
ev.preventDefault(); 
data=ev.dataTransfer.getData('Text'); 
if(ev.target=="[object HTMLLIElement]")  
{ev.target.parentNode.appendChild(document.getElementById(data));}
else{ev.target.appendChild(document.getElementById(data));} 
} 


Answer 16:

我想这个工具自己的文件上传框,当用户拖动文件到空间的框颜色会改变。

我发现了一个解决方案,JavaScript和CSS的一个漂亮的组合。 假设你有一个可投放区域div id为#drop 。 添加到您的Javascript:

$('#drop').on('dragenter', function() {
    $(this).addClass('dragover');
    $(this).children().addClass('inactive');
});

$('#drop').on('dragleave', function() {
    $(this).removeClass('dragover');
    $(this).children().removeClass('inactive');
});

然后,将它添加到您的CSS,灭活所有儿童类.inactive

#drop *.inactive {
    pointer-events: none;
}

因此,该子元素将是不活动的,只要在用户拖动的元件上的框。



Answer 17:

我并没有感到满足与任何这里提出的解决方法,因为我不想在孩子们元素失去控制。

所以我使用了不同的逻辑方法,将其转化为一个jQuery插件,叫jQuery的draghandler 。 它绝对不是操作DOM,确保高性能。 它的用法很简单:

$(document).ready(function() {

    $(selector).draghandler({
        onDragEnter: function() {
            // $(this).doSomething();
        },
        onDragLeave: function() {
            // $(this).doSomethingElse();
        }
    });

});

它的问题涉及完美不损害任何DOM功能。

下载,细节和其解释Git仓库 。



Answer 18:

实际上,我喜欢我所看到在https://github.com/lolmaus/jquery.dragbetter/但想分享一个可能的选择。 我总的策略是dragentering时,或(通过鼓泡)的任何孩子背景样式应用到悬浮窗(而不是它的孩子)。 然后我dragleaving下区时删除该样式。 移动到一个孩子的时候,即使我离开的时候它(dragleave火灾)从悬浮窗删除样式的想法是,我只想重新应用dragentering任何子风格父悬浮窗。 问题当然是从悬浮窗移动到悬浮窗的孩子时,为dragenter大干快上的dragleave前的孩子开枪,所以我的风格得到了应用无序。 解决方法对我来说是使用计时器迫使DragEnter事件回消息队列,让我来处理它的dragleave了。 我用了一个封闭对计时器回调访问事件。

$('.dropzone').on('dragenter', function(event) {
  (function (event) {
    setTimeout(function () {
      $(event.target).closest('.dropzone').addClass('highlight');
    }, 0);
  }) (event.originalEvent); 
});

这似乎在Chrome,IE,火狐工作,工作在悬浮窗孩子的数量无关。 我稍微感到不安超时保证事件的重新排序,但似乎工作得很好我的使用情况。



Answer 19:

这个答案可以在这里找到:

悬停的子元素时,HTML5 dragleave解雇

var counter = 0;

$('#drop').bind({
    dragenter: function(ev) {
        ev.preventDefault(); // needed for IE
        counter++;
        $(this).addClass('red');
    },

    dragleave: function() {
        counter--;
        if (counter === 0) { 
            $(this).removeClass('red');
        }
    }
});


Answer 20:

我想这个工具我自己,我不想jQuery的或任何的插件使用。

我想处理文件上传,按以下方式被认为最适合我:

文件结构:

--- /上传{上传目录}

--- /js/slyupload.js {JavaScript文件。}

---的index.php

--- upload.php的

--- styles.css的{只是一点点的造型..}

HTML代码:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>my Dzone Upload...</title>
<link rel="stylesheet" href="styles.css" />
</head>

<body>
    <div id="uploads"></div>        
    <div class="dropzone" id="dropzone">Drop files here to upload</div>     
    <script type="text/javascript" src="js/slyupload.js"></script>      
</body>
</html>

那么这是附着JavaScript文件::“JS / slyupload.js”

<!-- begin snippet:  -->

<!-- language: lang-js -->

    // JavaScript Document

    //ondragover, ondragleave...


    (function(){        

        var dropzone = document.getElementById('dropzone');
        var intv;

        function displayUploads(data){
            //console.log(data, data.length);
            var uploads = document.getElementById('uploads'),anchor,x;

            for(x=0; x < data.length; x++){

                //alert(data[x].file);
                anchor = document.createElement('a');
                anchor.href = data[x].file;
                anchor.innerText = data[x].name;

                uploads.appendChild(anchor);
            }               
        }

        function upload(files){
            //console.log(files);
            var formData = new FormData(), 
                xhr      = new XMLHttpRequest(),    //for ajax calls...
                x;                                  //for the loop..

                for(x=0;x<files.length; x++){
                    formData.append('file[]', files[x]);

                    /*//do this for every file...
                    xhr = new XMLHttpRequest();

                    //open... and send individually..
                    xhr.open('post', 'upload.php');
                    xhr.send(formData);*/
                }

                xhr.onload = function(){
                    var data = JSON.parse(this.responseText);   //whatever comes from our php..
                    //console.log(data);
                    displayUploads(data);

                    //clear the interval when upload completes... 
                    clearInterval(intv);
                }                   

                xhr.onerror = function(){
                    console.log(xhr.status);
                }

                //use this to send all together.. and disable the xhr sending above...

                //open... and send individually..
                intv = setInterval(updateProgress, 50);
                xhr.open('post', 'upload.php');
                xhr.send(formData);

                //update progress... 
                 /* */                   
        }

        function updateProgress(){
            console.log('hello');
         }          

        dropzone.ondrop = function(e){
            e.preventDefault(); //prevent the default behaviour.. of displaying images when dropped...
            this.className = 'dropzone';
            //we can now call the uploading... 
            upload(e.dataTransfer.files); //the event has a data transfer object...
        }

        dropzone.ondragover = function(){
            //console.log('hello');
            this.className = 'dropzone dragover';
            return false;
        }

        dropzone.ondragleave = function(){
            this.className = 'dropzone';
            return false;
        }           
    }(window));

CSS:

 body{ font-family:Arial, Helvetica, sans-serif; font-size:12px; } .dropzone{ width:300px; height:300px; border:2px dashed #ccc; color:#ccc; line-height:300px; text-align:center; } .dropzone.dragover{ border-color:#000; color:#000; } 



Answer 21:

使用greedy : true的孩子一样的功能, droppable 。 然后开始只有点击了第一层上的事件。



文章来源: 'dragleave' of parent element fires when dragging over children elements