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
Retrieving Html5 Video Duration Separately from the File
Jquery Event Won't Fire After Ajax Call
Angularjs to Output Plain Text Instead of Html
How to Dynamically Create CSS Class in JavaScript and Apply
How to Disable a Browser or Element Scrollbar, But Still Allow Scrolling With Wheel or Arrow Keys
Is $(Document).Ready() Also CSS Ready
How to Convert Unordered List into Nicely Styled ≪Select≫ Dropdown Using Jquery
How to Access and Process Nested Objects, Arrays, or Json
How to Loop Through or Enumerate a JavaScript Object
Object Comparison in JavaScript
How to Convert a Dom Node List to an Array in JavaScript
JavaScript Get Element by Name
How to Save a Base64 Image to User'S Disk Using JavaScript
Jquery Svg, Why Can't I Addclass
How to Remove a Style Added With .Css() Function
How to Get the Actual Rendered Font When It's Not Defined in Css