2010-06-29 40 views
19

我希望能夠在攜帶文件的光標進入瀏覽器窗口時立即突出顯示放置區域,這與Gmail執行操作的方式完全相同。但我無法做到這一點,我覺得我只是錯過了一些非常明顯的東西。如何檢測進入和離開窗口的HTML5拖動事件,例如Gmail?

我一直試圖做這樣的事情:

this.body = $('body').get(0) 
this.body.addEventListener("dragenter", this.dragenter, true) 
this.body.addEventListener("dragleave", this.dragleave, true)` 

但觸發該事件,只要將光標移動過來了比身體的其他元素,這是有道理的,但絕對不能正常工作。我可以在所有東西上放置一個元素,覆蓋整個窗口並對其進行檢測,但這會是一個可怕的方式。

我錯過了什麼?

+4

除了下面的答案:我注意到,至少在鉻事件的順序是: ENTER ENTER LEAVE ENTER LEAVE ... LEAVE 如果你繼續計數,這意味着的進入和離開,你將能夠區分初始輸入和內部輸入/輸出序列 PS:抱歉格式化了... – 2011-07-04 12:50:55

+0

你是男人@MartinWawrusch!感謝這 – aceofspades 2017-02-14 21:08:21

回答

1

您對addEventListener的第三個參數是true,這使偵聽器在捕獲階段運行(有關可視化的信息,請參閱http://www.w3.org/TR/DOM-Level-3-Events/#event-flow)。這意味着它將捕獲針對其後代的事件 - 並且意味着頁面上的所有元素。在你的處理程序中,你必須檢查它們觸發的元素是否是身體本身。我會給你我非常骯髒的做法。如果有人知道更簡單的方式,它實際上比較元素,我很樂意看到它。

this.dragenter = function() { 
    if ($('body').not(this).length != 0) return; 
    ... functional code ... 
} 

這找到了主體並從找到的元素集中刪除了this。如果該集合不是空的,this不是主體,所以我們不喜歡這個並返回。如果thisbody,則該集合將爲空,並執行代碼。

你可以嘗試一個簡單的if (this == $('body').get(0)),但這可能會慘敗。

0

您是否注意到在dropzone在Gmail中消失之前有延遲?我的猜測是,它們會在計時器(約500毫秒)內消失,並由dragover或某些此類事件重置。

您描述的問題的核心是,即使拖動到子元素中也會觸發dragleave。我試圖找到一種方法來檢測這一點,但我還沒有一個優雅乾淨的解決方案。

1

我自己遇到了麻煩,想出了一個可用的解決方案,但我並不想爲使用重疊而瘋狂。

添加ondragoverondragleaveondrop到窗口

添加ondragenterondragleaveondrop到覆蓋和目標元件

如果窗口或覆蓋上發生下降,則忽略它,而目標把手根據需要下降。我們需要重疊的原因是因爲每次元素被佔用時都會觸發ondragleave,所以覆蓋可以防止這種情況發生,同時拖放區被賦予更高的z-index,這樣文件就可以被丟棄。我正在使用一些在其他拖放相關問題中發現的代碼片段,所以我不能完全信任。下面是完整的HTML:

<!DOCTYPE html> 
<html> 
    <head> 
     <title>Drag and Drop Test</title> 
     <meta http-equiv="X-UA-Compatible" content="chrome=1" /> 
     <style> 
     #overlay { 
      display: none; 
      left: 0; 
      position: absolute; 
      top: 0; 
      z-index: 100; 
     } 
     #drop-zone { 
      background-color: #e0e9f1; 
      display: none; 
      font-size: 2em; 
      padding: 10px 0; 
      position: relative; 
      text-align: center; 
      z-index: 150; 
     } 
     #drop-zone.hover { 
      background-color: #b1c9dd; 
     } 
     output { 
      bottom: 10px; 
      left: 10px; 
      position: absolute; 
     } 
     </style> 
     <script> 
      var windowInitialized = false; 
      var overlayInitialized = false; 
      var dropZoneInitialized = false; 

      function handleFileSelect(e) { 
       e.preventDefault(); 

       var files = e.dataTransfer.files; 
       var output = []; 

       for (var i = 0; i < files.length; i++) { 
        output.push('<li>', 
         '<strong>', escape(files[i].name), '</strong> (', files[i].type || 'n/a', ') - ', 
         files[i].size, ' bytes, last modified: ', 
         files[i].lastModifiedDate ? files[i].lastModifiedDate.toLocaleDateString() : 'n/a', 
         '</li>'); 
       } 

       document.getElementById('list').innerHTML = '<ul>' + output.join('') + '</ul>'; 
      } 

      window.onload = function() { 
       var overlay = document.getElementById('overlay'); 
       var dropZone = document.getElementById('drop-zone'); 

       dropZone.ondragenter = function() { 
        dropZoneInitialized = true; 
        dropZone.className = 'hover'; 
       }; 
       dropZone.ondragleave = function() { 
        dropZoneInitialized = false; 
        dropZone.className = ''; 
       }; 
       dropZone.ondrop = function (e) { 
        handleFileSelect(e); 
        dropZoneInitialized = false; 
        dropZone.className = ''; 
       }; 

       overlay.style.width = (window.innerWidth || document.body.clientWidth) + 'px'; 
       overlay.style.height = (window.innerHeight || document.body.clientHeight) + 'px'; 
       overlay.ondragenter = function() { 
        if (overlayInitialized) { 
         return; 
        } 

        overlayInitialized = true; 
       }; 
       overlay.ondragleave = function() { 
        if (!dropZoneInitialized) { 
         dropZone.style.display = 'none'; 
        } 
        overlayInitialized = false; 
       }; 
       overlay.ondrop = function (e) { 
        e.preventDefault(); 
        dropZone.style.display = 'none'; 
       }; 

       window.ondragover = function (e) { 
        e.preventDefault(); 

        if (windowInitialized) { 
         return; 
        } 

        windowInitialized = true; 
        overlay.style.display = 'block'; 
        dropZone.style.display = 'block'; 
       }; 
       window.ondragleave = function() { 
        if (!overlayInitialized && !dropZoneInitialized) { 
         windowInitialized = false; 
         overlay.style.display = 'none'; 
         dropZone.style.display = 'none'; 
        } 
       }; 
       window.ondrop = function (e) { 
        e.preventDefault(); 

        windowInitialized = false; 
        overlayInitialized = false; 
        dropZoneInitialized = false; 

        overlay.style.display = 'none'; 
        dropZone.style.display = 'none'; 
       }; 
      }; 
     </script> 
    </head> 

    <body> 
     <div id="overlay"></div> 
     <div id="drop-zone">Drop files here</div> 
     <output id="list"><output> 
    </body> 
</html> 
20

我一個超時解決它(無發澀乾淨,但工程):

var dropTarget = $('.dropTarget'), 
    html = $('html'), 
    showDrag = false, 
    timeout = -1; 

html.bind('dragenter', function() { 
    dropTarget.addClass('dragging'); 
    showDrag = true; 
}); 
html.bind('dragover', function(){ 
    showDrag = true; 
}); 
html.bind('dragleave', function (e) { 
    showDrag = false; 
    clearTimeout(timeout); 
    timeout = setTimeout(function(){ 
     if(!showDrag){ dropTarget.removeClass('dragging'); } 
    }, 200); 
}); 

我的例子使用jQuery的,但它是沒有必要的。這裏是發生了什麼事情的總結:在dragenter和HTML(或機構)元素的dragover

  • 設置一個標誌(showDrag)到true
  • dragleave設置標誌爲false。然後設置一個簡短的超時來檢查該標誌是否仍然爲假。
  • 理想情況下,記錄超時並在設置下一個之前將其清除。

這樣,每個dragleave事件都會給DOM留出足夠的時間讓新的dragover事件重置該標誌。我們關心的真實,最終dragleave將會看到該標誌仍然是錯誤的。

+0

我把這個答案與Martin Wawrusch對這個問題的評論結合起來得到了最好的結果。每次處理任何事件時都會重置定時器,並且在dragleaves等於dragenters時完全取消定時器。這讓我在webkit瀏覽器中得到了即時的反饋,並且給了我一個短暫的firefox反饋,它不能可靠地在窗外跳出dragleave。 – Dave 2013-12-05 01:07:08

+0

換句話說,您_debounce_「dragleave」事件的處理程序。智能解決方法。 – 2017-11-27 10:55:31

3

@泰勒的回答是最好的!我已經提高了它。花了這麼多小時後,我得到了這個建議完全按照預期工作。

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

真的很抱歉張貼的東西,是角&強調具體,但我解決了這個問題的方式(HTML5規範,適用於Chrome)應便於觀察。

.directive('documentDragAndDropTrigger', function(){ 
return{ 
    controller: function($scope, $document){ 

    $scope.drag_and_drop = {}; 

    function set_document_drag_state(state){ 
     $scope.$apply(function(){ 
     if(state){ 
      $document.context.body.classList.add("drag-over"); 
      $scope.drag_and_drop.external_dragging = true; 
     } 
     else{ 
      $document.context.body.classList.remove("drag-over"); 
      $scope.drag_and_drop.external_dragging = false; 
     } 
     }); 
    } 

    var drag_enters = []; 
    function reset_drag(){ 
     drag_enters = []; 
     set_document_drag_state(false); 
    } 
    function drag_enters_push(event){ 
     var element = event.target; 
     drag_enters.push(element); 
     set_document_drag_state(true); 
    } 
    function drag_leaves_push(event){ 
     var element = event.target; 
     var position_in_drag_enter = _.find(drag_enters, _.partial(_.isEqual, element)); 
     if(!_.isUndefined(position_in_drag_enter)){ 
     drag_enters.splice(position_in_drag_enter,1); 
     } 
     if(_.isEmpty(drag_enters)){ 
     set_document_drag_state(false); 
     } 
    } 

    $document.bind("dragenter",function(event){ 
     console.log("enter", "doc","drag", event); 
     drag_enters_push(event); 
    }); 

    $document.bind("dragleave",function(event){ 
     console.log("leave", "doc", "drag", event); 
     drag_leaves_push(event); 
     console.log(drag_enters.length); 
    }); 

    $document.bind("drop",function(event){ 
     reset_drag(); 
     console.log("drop","doc", "drag",event); 
    }); 
    } 
}; 

})

我用一個列表來表示已引發一拖進入事件的元素。當一個拖曳事件發生時,我找到匹配的拖拽輸入列表中的元素,將它從列表中移除,並且如果結果列表爲空,我知道我已拖拽到文檔/窗口之外。

我需要在發生拖放事件後重置包含拖動元素的列表,或者下一次開始拖動列表時將使用最後一次拖放操作中的元素填充列表。

我只在目前爲止在chrome上測試過。我做到這一點是因爲Firefox和Chrome有不同的HTML5 DND API實現。 (拖放)。

真的希望這可以幫助一些人。

5

將事件添加到document似乎工作?經過Chrome,Firefox,IE 10測試。

獲取該事件的第一個元素是<html>,這應該沒問題。

var dragCount = 0, 
    dropzone = document.getElementById('dropzone'); 

function dragenterDragleave(e) { 
    e.preventDefault(); 
    dragCount += (e.type === "dragenter" ? 1 : -1); 
    if (dragCount === 1) { 
    dropzone.classList.add('drag-highlight'); 
    } else if (dragCount === 0) { 
    dropzone.classList.remove('drag-highlight'); 
    } 
}; 

document.addEventListener("dragenter", dragenterDragleave); 
document.addEventListener("dragleave", dragenterDragleave); 
7

不知道它這適用於所有的情況,但在我的情況下,它的工作非常出色

$('body').bind("dragleave", function(e) 
 
    { 
 
      if (!e.originalEvent.clientX && !e.originalEvent.clientY) 
 
      { 
 
      //outside body/window 
 
      } 
 
     }

+0

真正的答案,工作就像一個魅力! – Pinal 2016-03-25 16:21:42

+3

這並不總是成功地檢測到拖拽的結束(EG:從瀏覽器外部拖拽文件並將文件拖拽到Chrome上的「下載」欄中,不會以0,0作爲客戶端X/Y發送dragleave – 2016-05-11 18:19:59

0

當文件進入和離開子元素,它觸發額外dragenterdragleave,所以你需要上下數。

var count = 0 

document.addEventListener("dragenter", function() { 
    if (count === 0) { 
     setActive() 
    } 
    count++ 
}) 

document.addEventListener("dragleave", function() { 
    count-- 
    if (count === 0) { 
     setInactive() 
    } 
}) 

document.addEventListener("drop", function() { 
    if (count > 0) { 
     setInactive() 
    } 
    count = 0 
}) 
相關問題