[jquery] 在拖動子元素時,父元素的“dragleave”會觸發



Answers

我終於找到了一個我很滿意的解決方案。 我實際上找到了幾種方法來做我想做的事情,但沒有一個能夠像當前的解決方案一樣成功......在一種解決方案中,由於向#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中出現了問題,因為Firefox是雙調用dragenter所以我的計數器關閉了。 但是,它不是一個非常優雅的解決方案。

然後我偶然發現了這個問題: 如何在Firefox窗口外拖動時檢測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)。

Question

概觀

我有以下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">類的子元素上時,會為<div id="drag-n-drop">元素觸發dragenter事件,然後為<div id="dropzone">元素dragleave事件。

如果我再次將鼠標懸停在<div id="dropzone">元素上,則會再次觸發dragenter事件,這很酷,但隨後會為剛剛離開的子元素觸發dragleave事件,因此會執行removeClass指令,即不酷。

這種行為是有問題的,原因有二:

  1. 我只是將dragenterdragleave<div id="dropzone">所以我不明白為什麼兒童元素也附加了這些事件。

  2. 我仍然在將dragleave懸停在其<div id="dropzone">元素上時拖動它,所以我不想讓dragleave發射!

的jsfiddle

這裡有一個jsFiddle來修補: http://jsfiddle.net/yYF3S/2/ : http://jsfiddle.net/yYF3S/2/

所以...我怎麼能這樣做,當我通過<div id="dropzone">元素拖動文件時,即使拖動dragleave元素, dragleave也不會觸發......它應該只有在離開<div id="dropzone">元素時才會觸發dragleave在元素邊界內的任何位置懸停/拖動都不觸發dragleave事件。

我需要這是跨瀏覽器兼容的,至少在支持HTML5拖放的瀏覽器中,所以這個答案是不夠的。

看起來谷歌和Dropbox已經知道了這一點,但是他們的源代碼被縮小/複雜了,所以我沒有能夠從他們的實現中弄清楚這一點。




我的兩分錢:在你的dropzone上隱藏一個圖層,然後在你繪製時顯示它,然後將dragleave作為目標。

演示: https://jsfiddle.net/t6q4shat/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;
});



我的版本:

$(".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.currentTargete.relatedTargete.currentTarget e.relatedTargetdragoverdragleave事件的元素類轉儲。

它告訴我,在離開父塊( .dropzone )時, e.relatedTarget不是這個塊的子e.relatedTarget ,所以我知道我離開了dropzone。




我對這裡提出的任何解決方法都不滿意,因為我不想失去對孩子元素的控制。

所以我用了一種不同的邏輯方法,將它翻譯成一個名為jquery-draghandler的jQuery插件。 它絕對不會操縱DOM,保證高性能。 它的用法很簡單:

$(document).ready(function() {

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

});

它完美地處理問題而不影響任何DOM功能。

下載,關於其jquery-draghandler詳細信息和解釋。




我遇到了類似的問題,我用這種方法修復了它:

問題:當用戶在“拖放區域”(ul元素)中放置一個元素時,會觸發函數drop(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));} 
} 



超級簡單的快速修復,沒有廣泛測試,但現在在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閃爍錯誤,或者至少是導致我出現問題的排列。




問題是,你在dropzones內的元素當然是dropzone的一部分,當你進入孩子時,你離開父母。 解決這個問題並不容易。 您可能會嘗試向孩子添加事件,也會將您的班級再次添加到家長。

$("#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;
});

只為dropzone定義dragleave事件。




起初,我同意丟棄pointer-events: none辦法。 但後來我問自己:

你是否真的需要指針事件來處理拖拽過程中的子元素?

就我而言,我有很多東西在兒童身上,例如懸停以顯示其他動作的按鈕,內聯編輯等等。但是, 拖動過程中沒有必要或實際上甚至不需要這些。

就我而言,我使用類似這樣的方式來有選擇地關閉父容器的所有子節點的指針事件:

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

使用你最喜歡的方法在dragEnter / dragLeave事件處理程序中添加/刪除正在dragging-in-progress的類,就像我在dragStart執行相同的操作dragStart , 人。




正如benr在這個答案中所提到的,你可以防止子節點觸發事件,但是如果你需要綁定一些事件,可以這樣做:

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

並將其添加到您的JS代碼中:

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

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



我真的很喜歡我在https://github.com/lolmaus/jquery.dragbetter/看到的,但想分享一個可能的選擇。 我的一般策略是在拖放它或它的任何孩子時(通過冒泡)將背景樣式應用到拖放區(而不是其子區)。 然後,在拖動拖放區域時刪除樣式。 當移動到一個孩子的時候,即使我在離開它的時候從拖放區中移除了樣式(拖拉的火焰),我仍然只是將樣式重新應用於父區域,以便將任何孩子分散。 問題當然是,當從dropzone移動到dropzone的孩子時,dragicter會在dragleave之前觸發孩子,所以我的樣式按順序亂用。 對我來說,解決方案是使用計時器強制dragenter事件回到消息隊列,讓我在dragleave之後處理它。 我使用閉包來訪問定時器回調中的事件。

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

這似乎工作在鉻,即Firefox和工程,無論在dropzone的兒童數量。 我對保證事件重新排序的超時有些不安,但它似乎對我的使用情況非常有效。






Related