'dragleave' of parent element fires when dragging over children elements 'dragleave' of parent element fires when dragging over children elements jquery jquery

'dragleave' of parent element fires when dragging over children elements


If you don't need to bind events to the child elements, you can always use the pointer-events property.

.child-elements {  pointer-events: none;}


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


This is a little ugly but it works dammit!...

On your 'dragenter' handler store the event.target (in a variable inside your closure, or whatever), then in your 'dragleave' handler only fire your code if event.target === the one you stored.

If your 'dragenter' is firing when you don't want it to (i.e. when it's entering after leaving child elements), then the last time it fires before the mouse leaves the parent, it's on the parent, so the parent will always be the final 'dragenter' before the intended 'dragleave'.

(function () {    var droppable = $('#droppable'),        lastenter;    droppable.on("dragenter", function (event) {        lastenter = event.target;        droppable.addClass("drag-over");                });    droppable.on("dragleave", function (event) {        if (lastenter === event.target) {            droppable.removeClass("drag-over");        }    });}());