2010-06-29 7 views
19

मैं ड्रॉप क्षेत्र को हाइलाइट करने में सक्षम होना चाहता हूं जैसे ही कर्सर एक फ़ाइल लेता है, ब्राउज़र विंडो में प्रवेश करता है, ठीक उसी तरह जीमेल करता है। लेकिन मैं इसे काम नहीं कर सकता, और मुझे लगता है कि मैं वास्तव में कुछ स्पष्ट रूप से याद कर रहा हूं।जीमेल की तरह खिड़की में प्रवेश करने और छोड़ने के लिए एचटीएमएल 5 ड्रैग इवेंट का पता कैसे लगा सकता हूं?

मैं इस तरह कुछ करने के लिए कोशिश कर रखना:

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

लेकिन जब भी कर्सर और शरीर के अलावा अन्य तत्वों से बाहर ले जाता है, जो समझ में आता है, लेकिन पूरी तरह से काम नहीं करता है कि घटनाओं सक्रिय करता है। मैं सब कुछ के ऊपर एक तत्व रख सकता हूं, पूरी खिड़की को कवर कर सकता हूं और उस पर पता लगा सकता हूं, लेकिन यह इसके बारे में जाने का एक भयानक तरीका होगा।

मुझे क्या याद आ रही है?

+4

नीचे दिए गए उत्तर के अलावा: मैंने देखा है कि कम से कम क्रोम के तहत घटनाओं के अनुक्रम है: ENTER ENTER अवकाश ENTER अवकाश ... अवकाश जिसका मतलब है कि अगर आप गणना प्रवेश और पत्तियों के आप प्रारंभिक प्रविष्टि और आंतरिक प्रवेश/छोड़ने के दृश्यों के बीच अंतर करने में सक्षम होंगे पीएस: फेड अप स्वरूपण के लिए खेद है ... –

+0

आप आदमी @ मार्टिन वावरशच हैं! इस – aceofspades

उत्तर

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

क्या आपने देखा है कि जीमेल में ड्रॉपज़ोन गायब होने से पहले देरी हो रही है? मेरा अनुमान है कि वे एक टाइमर (~ 500ms) पर गायब हो गए हैं जो ड्रैगओवर या ऐसी कुछ घटनाओं द्वारा रीसेट हो जाता है।

आपके द्वारा वर्णित समस्या का मूल यह है कि जब आप किसी बच्चे तत्व में खींचते हैं तो ड्रैगलेव ट्रिगर होता है। मैं इसका पता लगाने का एक तरीका खोजने की कोशिश कर रहा हूं, लेकिन मेरे पास अभी तक एक सुंदर साफ समाधान नहीं है।

1

मुझे अपने आप में परेशानी हो रही थी और एक उपयोगी समाधान के साथ आया, हालांकि मैं ओवरले का उपयोग करने के बारे में पागल नहीं हूं।

जोड़े ondragover, ondragleave और खिड़की

को ondrop एक ओवरले और एक लक्ष्य तत्व

ड्रॉप खिड़की या ओवरले पर होता है, यह नजरअंदाज कर दिया है करने के लिए ondragenter, ondragleave और ondrop जोड़े, जबकि लक्ष्य हैंडल वांछित के रूप में ड्रॉप। कारण हमें ओवरले की आवश्यकता है क्योंकि ondragleave प्रत्येक तत्व को हर बार ट्रिगर करता है, इसलिए ओवरले रोकता है कि होने से, जबकि ड्रॉप ज़ोन को उच्च जेड-इंडेक्स दिया जाता है ताकि फ़ाइलों को हटाया जा सके। मैं अन्य ड्रैग और ड्रॉप संबंधित प्रश्नों में पाए गए कुछ कोड स्निपेट का उपयोग कर रहा हूं, इसलिए मैं पूर्ण क्रेडिट नहीं ले सकता।यहाँ पूर्ण 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 का उपयोग करती है, लेकिन यह आवश्यक नहीं है।

  • एक ध्वज (showDrag) सेट dragenter और html (या शरीर) तत्व के dragover पर true करने के लिए: यहाँ एक क्या हो रहा है के सारांश है।
  • dragleave पर ध्वज को false पर सेट करें। फिर यह जांचने के लिए एक संक्षिप्त टाइमआउट सेट करें कि ध्वज अभी भी झूठा है या नहीं।
  • आदर्श रूप से, टाइमआउट का ट्रैक रखें और अगली सेटिंग सेट करने से पहले इसे साफ़ करें।

इस तरह, प्रत्येक dragleave घटना एक नया dragover ध्वज रीसेट करने के लिए घटना के लिए डोम पर्याप्त समय देता है। वास्तविक, अंतिमdragleave जो हम देखते हैं, यह देखेंगे कि ध्वज अभी भी झूठा है।

+0

के लिए धन्यवाद, इस सवाल पर मार्टिन वावरश की टिप्पणी के साथ मुझे इस जवाब को जोड़ने से सर्वोत्तम परिणाम मिले। किसी भी घटना को संभालने पर टाइमर रीसेट होता है, और जब ड्रैगलेव बराबर ड्रैगेंटर होता है तो पूरी तरह से रद्द कर दिया जाता है। यह मुझे वेबकिट ब्राउज़र में तत्काल प्रतिक्रिया देता है, और मुझे फ़ायरफ़ॉक्स में एक संक्षिप्त अंतराल के साथ प्रतिक्रिया देता है, जो खिड़की से बाहर निकलने पर विश्वसनीय रूप से ड्रैगलेव को कॉल नहीं करता है। – Dave

+0

दूसरे शब्दों में, आप 'ड्रैगलेव' ईवेंट के हैंडलर _debounce_। स्मार्ट वर्कअराउंड –

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

वास्तव में खेद है कि कोणीय है & विशिष्ट रेखांकित, हालांकि जिस तरह से मैं समस्या हल (एचटीएमएल 5 कल्पना, क्रोम पर काम करता है) का पालन करने के लिए आसान होना चाहिए पोस्ट करने के लिए।

.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); 
    }); 
    } 
}; 

})

मैं तत्वों है कि एक खींचें घटना दर्ज ट्रिगर किया है प्रतिनिधित्व करने के लिए एक सूची का उपयोग करें। जब एक ड्रैग छोड़ने की घटना होती है तो मुझे मेल खाने वाली सूची में तत्व मिल जाता है जो मेल खाता है, इसे सूची से हटा दें, और यदि परिणामस्वरूप सूची खाली है तो मुझे पता है कि मैंने दस्तावेज़/विंडो के बाहर खींच लिया है।

मुझे ड्रॉप ईवेंट होने के बाद तत्वों पर खींचा गया सूची रीसेट करने की आवश्यकता है, या अगली बार जब मैं कुछ खींचने शुरू करता हूं तो सूची अंतिम ड्रैग और ड्रॉप एक्शन के तत्वों के साथ पॉप्युलेट हो जाएगी।

मैंने अभी तक क्रोम पर इसका परीक्षण किया है। मैंने इसे इसलिए बनाया क्योंकि फ़ायरफ़ॉक्स और क्रोम में एचटीएमएल 5 डीएनडी के विभिन्न एपीआई कार्यान्वयन हैं। (खींचें और छोड़ें)।

वास्तव में उम्मीद है कि इससे कुछ लोगों की मदद मिलती है।

5

document पर ईवेंट जोड़ना काम करना प्रतीत होता था? क्रोम, फ़ायरफ़ॉक्स, आईई 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

+3

यह ड्रैग के अंत का पता लगाने में हमेशा सफल नहीं होता है (ईजी: ब्राउजर के बाहर से खींचा गया फ़ाइल और क्रोम में 'डाउनलोड' बार पर गिरा दिया गया है, क्लाइंटएक्स/वाई के रूप में 0,0 के साथ ड्रैगलेव नहीं भेजता है –

0

फ़ाइल में प्रवेश करती है और बच्चे के तत्वों को छोड़ देता है जब काम किया यह अतिरिक्त आग dragenter और dragleave इसलिए आपको ऊपर और नीचे गिनने की आवश्यकता है।

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 
})