Anon is (mostly) correct. Simply put: when the mouse moves around the edge of an element inside your target, you get a dropenter for the element under the cursor and dropleave for the element that was under the cursor earlier. This happens for absolutely any descendant.
You cannot check the dragleave related dragleave , because if you move the mouse from the target to the child, you will get a dropenter for the child and then dropleave for the target! This is ridiculous, and I donβt understand how useful it is.
Here's a crappy jQuery-based solution that I came up with some time ago.
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); });
The trick is based on two facts:
- When you move the mouse from grandchild to child, both
dragenter and dragleave will be queued for the target element - in that order. dragenter and dragleave are queued.
So here is what happens.
- In the
dragenter event dragenter I set some common variable to indicate that the drag movement is not finished yet. - I use
setTimeout with a zero delay to immediately change this variable. - But! Since two events are in the queue at the same time, the browser will not start any scheduled functions until both events complete the solution. So the next thing that happens is the
dragleave event dragleave . - If the
dragleave sees that it was paired with the dragenter on the same target element, this means that the mouse must move from some descendant to another offspring. Otherwise, the mouse actually leaves the target element. - Then
setTimeout finally resolves to zero seconds later, returning the variable before another event occurs.
I canβt come up with a simpler approach.
source share