Html5 Dragleave Fired When Hovering a Child Element

dragleave' of parent element fires when dragging over children elements

I finally found a solution I'm happy with. I actually found several ways to do what I want but none were as successful as the current solution... in one solution, I experienced frequent flickering as a result of adding/removing a border to the #dropzone element... in another, the border was never removed if you hover away from the browser.

Anyway, my best hacky solution is this:

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

This works pretty well but issues came up in Firefox because Firefox was double-invoking dragenter so my counter was off. But nevertheless, its not a very elegant solution.

Then I stumbled upon this question: How to detect the dragleave event in Firefox when dragging outside the window

So I took the answer and applied it to my situation:

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

This is clean and elegant and seems to be working in every browser I've tested so far (haven't tested IE yet).

Why is dragleave event firing unexpectedly?

I figured out the issue myself.

Cause

Drag events bubble up the chain. So, it struck me that because of that when I made my overlay visible, it triggered dragenter on that and maybe on further dragging the dragleave is being triggered by its child which bubbles up to the parent where I am listening.

However, it turns out that dragleave is triggered whenever dragenter is triggered, even if the dragenter target is a child! So, in my case the moment I made the overlay visible, dragenter was triggered on it and dragleave was triggered on document and window.

The fix

So an ideal drop target must not have any children, so that dragging within itself does not trigger dragleave. In my case the drop target overlay takes the full window space so in my case it is enough to listen for dragleave only on the overlay DOM. This fixed 90% of myc problems.

However, there was the problem when user dragged over the child div inside that. The fix for that was too easy. My requirement was that I make the child div invisible to mouse events. For that we have the magic CSS - pointer-events. Setting this property to none does the trick. The only downside is that this is not supported in IE until 11.

See demo http://jsfiddle.net/fSB32/5/

If you do need to support IE below 11, then one trick could be used to put an empty div as the child in the drop target and make sure it has the highest z-index and it covers the full drop target. This way drag events should be directed only to this DOM as all other children are visible "through" it.

How to stop onDragLeave from firing when moving from container to children in React

Found the answer :). Ultimately this is more of a javascript/jquery question, rather than a react question. Answer is gotten from HTML5 dragleave fired when hovering a child element.

For it to work with React, just add the logic in componentDidMount so we can attach dragenter/dragleave on our container when it is rendered

...
componentDidMount: function(){
var thisComponent = this;
var counter = 0;

$(this.refs.container).bind({
dragenter: function(ev) {
ev.preventDefault(); // needed for IE
counter++;
thisComponent._testOver();
},

dragleave: function() {
counter--;
if (counter === 0) {
thisComponent._testOut();
}
}
});
},
...


Related Topics



Leave a reply



Submit