Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>The anon is (mostly) correct. To put it simply: when the mouse moves over the <em>edge</em> of an element inside your drop target, you get a <code>dropenter</code> for the element <em>under the cursor</em> and a <code>dropleave</code> for the element that was under the cursor previously. This happens for absolutely any descendant.</p> <p>You can't check the element associated with <code>dragleave</code>, because if you move the mouse <em>from</em> your drop target <em>onto</em> a child element, you'll get a <code>dropenter</code> for the child and then a <code>dropleave</code> for the target! It's kind of ridiculous and I don't see how this is a useful design at all.</p> <p>Here's a crappy jQuery-based solution I came up with some time ago.</p> <pre><code>var $drop_target = $(document.body); var within_enter = false; $drop_target.bind('dragenter', function(evt) { // Default behavior is to deny a drop, so this will allow it evt.preventDefault(); within_enter = true; setTimeout(function() { within_enter = false; }, 0); // This is the part that makes the drop area light up $(this).addClass('js-dropzone'); }); $drop_target.bind('dragover', function(evt) { // Same as above evt.preventDefault(); }); $drop_target.bind('dragleave', function(evt) { if (! within_enter) { // And this makes it un-light-up :) $(this).removeClass('js-dropzone'); } within_enter = false; }); // Handle the actual drop effect $drop_target.bind('drop', function(evt) { // Be sure to reset your state down here $(this).removeClass('js-dropzone'); within_enter = false; evt.preventDefault(); do_whatever(evt.originalEvent.dataTransfer.files); }); </code></pre> <p>The trick relies on two facts:</p> <ul> <li>When you move the mouse from a grandchild into a child, both <code>dragenter</code> and <code>dragleave</code> will be queued up for the target element—<em>in that order</em>.</li> <li>The <code>dragenter</code> and <code>dragleave</code> are queued <em>together</em>.</li> </ul> <p>So here's what happens.</p> <ul> <li>In the <code>dragenter</code> event, I set some shared variable to indicate that the drag movement hasn't finished resolving yet.</li> <li>I use <code>setTimeout</code> with a delay of zero to immediately change that variable back.</li> <li>But! Because the two events are queued at the exact same time, the browser won't run any scheduled functions until <em>both</em> events have finished resolving. So the next thing that happens is <code>dragleave</code>'s event handler.</li> <li>If <code>dragleave</code> sees that it was paired with a <code>dragenter</code> on the same target element, that means the mouse <em>must</em> have moved from some descendant to some other descendant. Otherwise, the mouse is actually leaving the target element.</li> <li>Then the <code>setTimeout</code> finally resolves zero seconds later, setting back the variable before another event can come along.</li> </ul> <p>I can't think of a simpler approach.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload