HTML5 dragleave fired when hovering a child element

102,600

Solution 1

You just need to keep a reference counter, increment it when you get a dragenter, decrement when you get a dragleave. When the counter is at 0 - remove the class.

var counter = 0;

$('#drop').bind({
    dragenter: function(ev) {
        ev.preventDefault(); // needed for IE
        counter++;
        $(this).addClass('red');
    },

    dragleave: function() {
        counter--;
        if (counter === 0) { 
            $(this).removeClass('red');
        }
    }
});

Note: In the drop event, reset counter to zero, and clear the added class.

You can run it here

Solution 2

Is it possible to prevent dragleave from firing when dragging into a child element?

Yes.

#drop * {pointer-events: none;}

That CSS seem to be enough for Chrome.

While using it with Firefox, the #drop shouldn't have text nodes directly (else there's a strange issue where a element "leave it to itself"), so I suggest to leave it with only one element (e.g., use a div inside #drop to put everything inside)

Here's a jsfiddle solving the original question (broken) example.

I've also made a simplified version forked from the @Theodore Brown example, but based only in this CSS.

Not all browsers have this CSS implemented, though: http://caniuse.com/pointer-events

Seeing the Facebook source code I could find this pointer-events: none; several times, however it's probably used together with graceful degradation fallbacks. At least it's so simple and solves the problem for a lot of environments.

Solution 3

It has been quite some time after this question is asked and a lot of solutions (including ugly hacks) are provided.

I managed to fix the same problem I had recently thanks to the answer in this answer and thought it may be helpful to someone who comes through to this page. The whole idea is to store the evenet.target in ondrageenter everytime it is called on any of the parent or child elements. Then in ondragleave check if the current target (event.target) is equal to the object you stored in ondragenter.

The only case these two are matched is when your drag is leaving the browser window.

The reason that this works fine is when the mouse leaves an element (say el1) and enters another element (say el2), first the el2.ondragenter is called and then el1.ondragleave. Only when the drag is leaving/entering the browser window, event.target will be '' in both el2.ondragenter and el1.ondragleave.

Here is my working sample. I have tested it on IE9+, Chrome, Firefox and Safari.

(function() {
    var bodyEl = document.body;
    var flupDiv = document.getElementById('file-drop-area');

    flupDiv.onclick = function(event){
        console.log('HEy! some one clicked me!');
    };

    var enterTarget = null;

    document.ondragenter = function(event) {
        console.log('on drag enter: ' + event.target.id);
        enterTarget = event.target;
        event.stopPropagation();
        event.preventDefault();
        flupDiv.className = 'flup-drag-on-top';
        return false;
    };

    document.ondragleave = function(event) {
        console.log('on drag leave: currentTarget: ' + event.target.id + ', old target: ' + enterTarget.id);
        //Only if the two target are equal it means the drag has left the window
        if (enterTarget == event.target){
            event.stopPropagation();
            event.preventDefault();
            flupDiv.className = 'flup-no-drag';         
        }
    };
    document.ondrop = function(event) {
        console.log('on drop: ' + event.target.id);
        event.stopPropagation();
        event.preventDefault();
        flupDiv.className = 'flup-no-drag';
        return false;
    };
})();

And here is a simple html page:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Multiple File Uploader</title>
<link rel="stylesheet" href="my.css" />
</head>
<body id="bodyDiv">
    <div id="cntnr" class="flup-container">
        <div id="file-drop-area" class="flup-no-drag">blah blah</div>
    </div>
    <script src="my.js"></script>
</body>
</html>

With proper styling what I have done is to make the inner div (#file-drop-area) much bigger whenever a file is dragged into the screen so that the user can easily drop the files into the proper place.

Solution 4

Here, the simplest Cross-Browser solution (seriously):

jsfiddle <-- try dragging some file inside the box

You can do something like that:

var dropZone= document.getElementById('box');
var dropMask = document.getElementById('drop-mask');

dropZone.addEventListener('dragover', drag_over, false);
dropMask.addEventListener('dragleave', drag_leave, false);
dropMask.addEventListener('drop', drag_drop, false);

In a few words, you create a "mask" inside the dropzone, with width & height inherited, position absolute, that will just show when the dragover starts.
So, after showing that mask, you can do the trick by attaching the others dragleave & drop events on it.

After leaving or dropping, you just hide the mask again.
Simple and without complications.

(Obs.: Greg Pettit advice -- You must be sure that the mask hover the entire box, including the border)

Solution 5

The "right" way to solve this issue is to disable pointer events on child elements of the drop target (as in @H.D.'s answer). Here's a jsFiddle I created which demonstrates this technique. Unfortunately, this doesn't work in versions of Internet Explorer prior to IE11, since they didn't support pointer events.

Luckily, I was able to come up with a workaround which does work in old versions of IE. Basically, it involves identifying and ignoring dragleave events which occur when dragging over child elements. Because the dragenter event is fired on child nodes before the dragleave event on the parent, separate event listeners can be added to each child node which add or remove an "ignore-drag-leave" class from the drop target. Then the drop target's dragleave event listener can simply ignore calls which occur when this class exists. Here's a jsFiddle demonstrating this workaround. It is tested and working in Chrome, Firefox, and IE8+.

Update:

I created a jsFiddle demonstrating a combined solution using feature detection, where pointer events are used if supported (currently Chrome, Firefox, and IE11), and the browser falls back to adding events to child nodes if pointer event support isn't available (IE8-10).

Share:
102,600
pimvdb
Author by

pimvdb

Updated on July 08, 2022

Comments

  • pimvdb
    pimvdb almost 2 years

    The problem I'm having is that the dragleave event of an element is fired when hovering a child element of that element. Also, dragenter is not fired when hovering back the parent element again.

    I made a simplified fiddle: http://jsfiddle.net/pimvdb/HU6Mk/1/.

    HTML:

    <div id="drag" draggable="true">drag me</div>
    
    <hr>
    
    <div id="drop">
        drop here
        <p>child</p>
        parent
    </div>
    

    with the following JavaScript:

    $('#drop').bind({
                     dragenter: function() {
                         $(this).addClass('red');
                     },
    
                     dragleave: function() {
                         $(this).removeClass('red');
                     }
                    });
    
    $('#drag').bind({
                     dragstart: function(e) {
                         e.allowedEffect = "copy";
                         e.setData("text/plain", "test");
                     }
                    });
    

    What it is supposed to do is notifying the user by making the drop div red when dragging something there. This works, but if you drag into the p child, the dragleave is fired and the div isn't red anymore. Moving back to the drop div also doesn't make it red again. It's necessary to move completely out of the drop div and drag back into it again to make it red.

    Is it possible to prevent dragleave from firing when dragging into a child element?

    2017 Update: TL;DR, Look up CSS pointer-events: none; as described in @H.D.'s answer below that works in modern browsers and IE11.

  • pimvdb
    pimvdb almost 13 years
    Thanks a lot, but indeed it's Chrome I'm trying to solve this problem in.
  • robertc
    robertc almost 13 years
    @pimvdb I see you've logged a bug, I'll just leave a reference to it here in case anyone else comes across this answer.
  • pimvdb
    pimvdb almost 13 years
    I did indeed, but I forgot to add a link to it here. Thanks for doing that.
  • pimvdb
    pimvdb almost 12 years
    Thanks! I can't get this to work in Chrome however. Could you provide a working fiddle of your hack?
  • Aldekein
    Aldekein over 11 years
    Does it go into «if (typeof event.clientX === 'undefined')»?
  • Greg Pettit
    Greg Pettit almost 11 years
    Not sure why, but it's not working consistently with Chrome. Sometimes leaving the area keeps the mask visible.
  • Greg Pettit
    Greg Pettit almost 11 years
    Actually, it's the border. Have the mask overlapping the border, with no border of its own, and it should work OK.
  • Christof
    Christof almost 11 years
    I was thinking of doing it by checking the coords too. You did most of the work for me, thx :). I had to make some adjustments though: if (e.x >= (rect.left + rect.width) || e.x <= rect.left || e.y >= (rect.top + rect.height) || e.y <= rect.top)
  • H.D.
    H.D. almost 11 years
    Worked nicely but there could be another window over the browser, so getting the mouse location and comparing it to the rectangular screen area isn't enough.
  • Hengjie
    Hengjie over 10 years
    It won't work in Chrome because it's event doesn't have e.x and e.y.
  • Hengjie
    Hengjie over 10 years
    This doesn't appear to always work in Chrome. I would on occasion receive clientX above 0 when the mouse is outside of the box. Granted, my elements are position:absolute
  • Profet
    Profet over 10 years
    Does it happens all the time or only sometimes ? Because if the mouse is moving too fast (ig. outside the window), you might get wrong values.
  • Hengjie
    Hengjie over 10 years
    It happens 90% of the time. There are rare cases (1 out of 10 times) where I can make it reach 0. I'll try again moving the mouse slower, but I couldn't say I was moving quickly (perhaps what you'd call normal speed).
  • H.D.
    H.D. over 10 years
    This answer shows a workaround for the undesirable firing but neglects the question "Is it possible to prevent dragleave from firing when dragging into a child element?" entirely.
  • H.D.
    H.D. over 10 years
    Behaviour can get strange when dragging a file from outside the browser. In Firefox I've got "Entering child -> Entering Parent -> Leaving child -> Entering child -> Leaving child" without leaving the parent, which left with the "over" class. Old IE would need an attachEvent replacement for the addEventListener.
  • H.D.
    H.D. over 10 years
    That solution depends strongly on the bubbling, the "false" in all addEventListener should be emphasized as essential (although that's the default behaviour), since many people may not know about that.
  • H.D.
    H.D. over 10 years
    That single draggable adds an effect to the dropzone that doesn't appear when the dropzone is used for other draggable objects that doesn't trigger the dragstart event. Perhaps using everything as a dropzone for the effect of dragging while keeping the real dropzone with other handlers would do that.
  • Theodore Brown
    Theodore Brown over 10 years
    @H.D. I updated my answer with info on using the pointer-events CSS property to prevent the dragleave event from firing in Chrome, Firefox, and IE11+. I also updated my other workaround to support IE8 and IE9, in addition to IE10. It is intentional that the dropzone effect is only added when dragging the "Drag me" link. Others can feel free to change this behavior as needed to support their use cases.
  • Theodore Brown
    Theodore Brown over 10 years
    The pointer-events property is the right solution going forward, but unfortunately it doesn't work in IE8-IE10, which are still widely used. Also, I should point out that your current jsFiddle doesn't even work in IE11, since it doesn't add the necessary event listeners and default behavior prevention.
  • null
    null over 10 years
    Only thing this snippet does is preventing the 'hovered' class to be removed by reapplying it the next tick cycle (making the 'dragleave' event useless).
  • Marcin Raczkowski
    Marcin Raczkowski over 10 years
    It's not useless. If you leave parent it'll work as expected. The power of this solution is it's simplicity, it's not ideal or best there is. Better solution would be to mark an enter on a child, in onleave check if we just entered child, and if so not trigger leave event. It'll hovever need testing, extra guards, checing for grandchildren, etc.
  • Nicole Stutz
    Nicole Stutz over 10 years
    I like this solution. Didn't work in Firefox first. But if you replace e.x with e.clientX and e.y with e.clientY it works. Also works in Chrome.
  • entropy
    entropy over 10 years
    Note, your jsfiddle has a bug in it, in drag_drop, you should remove the hover class on "#box" not "#box-a"
  • Timo Huovinen
    Timo Huovinen about 10 years
    Did not work in chrome for me, neither did what Chris or Daniel Stuts suggested
  • Timo Huovinen
    Timo Huovinen about 10 years
    Occasionally does not work on chrome (does not catch dragleave properly)
  • standac
    standac about 10 years
    This will prevent childs from triggering "click" event.
  • bingjie2680
    bingjie2680 almost 10 years
    This is a nice solution, but somehow it did not work for me. I finally figure out a workaround. For those of you who are looking for something else. you could try this: github.com/bingjie2680/jquery-draghover
  • Arthur Corenzan
    Arthur Corenzan almost 10 years
    OMG this is the most obvious solution and only had ONE vote... Come on people, you can do better. I was thinking about that but after seeing the level of sophistication of the first few answers I almost discarded it. Did you have any drawbacks ?
  • floribon
    floribon almost 10 years
    Using pointer-events is indeed a good answer, I struggled a bit before finding out by myself, that answer should be higher.
  • Woody
    Woody over 9 years
    Hi @ArthurCorenzan - thanks. No - it works fine, actually i figured out an even simpler way to do it, shown above.
  • Arthur Corenzan
    Arthur Corenzan over 9 years
    Oh no, you just blew my first comment :P I liked the previous solution better (preventing the bubbling of dragleave event of child elements).
  • Woody
    Woody over 9 years
    The first solution did not work generically e.g. if you had nested child elements, this way does and it's less code, a "win win" :)
  • Woody
    Woody over 9 years
    Checkout this useful library for html5 drag and drop : github.com/stevendwood/html5-dropzone
  • Arthur Corenzan
    Arthur Corenzan about 9 years
    This didn't work when the edge of the element being dragged touches the edge of another draggable element. For instance a sortable list; dragging the element downwards, over the next draggable item does not decrement the counter back to 0, instead it gets stucked at 1. But if I drag it sideways, out of any other draggable element, it works. I went with the pointer-events: none on the children. When I start dragging I append a class that has this property, when drag's over I remove the class. Worked nicely on Safari and Chrome, but not on Firefox.
  • Damien
    Damien about 9 years
    That worked for me with Firefox 37 and Chromium 39, but not with IE11 (there are more dragenter than dragleave events). Actually, even the fiddle does not work with IE11.
  • Damien
    Damien about 9 years
    Adding e.preventDefault() in dragenter(e) seemed to fix it for IE11. I got the idea while reading this: The HTML5 drag and drop disaster
  • Woody
    Woody about 9 years
    I updated the link. The reason it doesn't work on IE is because of the "text/plain" content type in the drag start once that's fixed it then refuses to fire any dragleave events if you don't cancel the dragenter event...another quirk
  • Luca Fagioli
    Luca Fagioli about 9 years
    Dangerous hack. I've made a jQuery plugin that do the job simply. Check my answer
  • Luca Fagioli
    Luca Fagioli about 9 years
    No. I do not want to lose the control over the child elements. I've made a simple jQuery plugin that deals with the problem doing the job for us. Check my answer.
  • Woody
    Woody about 9 years
    @LucaFagioli - in what way is it a hack, and why would it be dangerous ?
  • Luca Fagioli
    Luca Fagioli about 9 years
    @Woody It's a hack because to make it work in IE you need to add a special condition. Moreover you need a global variable for each selector you want to be firing the dragenter event, which mess your code. Finally, it's dangerous because if for some reason you need to call stopPropagation() on a child element this solution breaks.
  • Woody
    Woody about 9 years
    First of all, if you think you have a better answer to the posted problem, then write an answer, not a comment against the highest voted answer trying to tell everyone how smart you are and how dumb the answer and everyone who thinks it's the best answer is. Second of all, your fiddle obviously doesn't work since it only uses one counter to track the events on two elements, i mean how would you expect the code in the answer to work in that instance !? Clearly you need to track the events per element you want to listen on, which is what the questions asks.
  • ZachB
    ZachB about 9 years
    If the user changes their mind and drags the file back out of the browser, this will remain red.
  • cupcakekid
    cupcakekid almost 9 years
    Hacky but clever. I like. Worked for me.
  • M Katz
    M Katz almost 9 years
    Very useful (at least for me where I only care about Chrome).
  • stonea
    stonea almost 9 years
    In chrome use e.originalEvent.x and e.originalEvent.y in place of e.x and e.y.
  • Kirby
    Kirby over 8 years
    Oh, how I wish this answer had more votes! Thank you
  • Luke Hansford
    Luke Hansford over 8 years
    Fantastic option for those of us lucky enough to not have to support old browsers.
  • Fred
    Fred over 8 years
    This is the best solution, it's better than the counter (especially if you delegate events) and it works with draggable children as well.
  • caub
    caub over 8 years
    doesn't work when leaving by the left side (when you have no margin)
  • Pipo
    Pipo over 8 years
    This worked for me in all browsers, but Firefox. I have a new solution which works everywhere in my case. On the first dragenter I save event.currentTarget in a new variable dragEnterTarget. As long as dragEnterTarget is set, I ignore further dragenter events, because they are from children. In all dragleave events I check dragEnterTarget === event.target. If this is false the event will be ignored as it was fired by a child. If this is true I reset dragEnterTarget to undefined.
  • Liam Johnston
    Liam Johnston over 8 years
    Great solution. I needed to reset the counter to 0 in the function that handles accepting the drop though, otherwise subsequent drags didn't work as expected.
  • mpen
    mpen over 8 years
    This is what I ended up doing too, but it's still sketchy.
  • marcelj
    marcelj over 8 years
    Note that this does not work if any child stops propagation of the events. Here's my solution with document.elementFromPoint, which is even simpler: stackoverflow.com/a/35117158/1620264 (I haven't really used elementFromPoint before, so not 100% sure my solution is correct)
  • David
    David about 8 years
    What if your children is a Button? o.O
  • BurninLeo
    BurninLeo almost 8 years
    Works great - and you can still add the counter solution for older browsers. Thanks!
  • Jon Abrams
    Jon Abrams almost 8 years
    This didn't work for me out of the box. I needed to use event.clientX, event.clientY instead, since they're relative to the viewport and not the page. I hope that helps another lost soul out there.
  • B T
    B T almost 8 years
    This won't work if you've already dropped something. The counters will become out of sync - you need a way to determine if its a new drag or a continuation of an old drag.
  • B T
    B T almost 8 years
    This doesn't help tho for the problem of duplicate dragenter events
  • Woody
    Woody almost 8 years
    @BT didn't Liam Johnston's reset in the drop function help ? If not you could try adding a dragend listener that resets the counter. I think that dragend should fire on the thing being dragged, but the event should propagate so that you can catch it at the body.
  • B T
    B T almost 8 years
    @Woody I didn't see his comment in the huge list of comments here, but yes, that's the fix. Why not incorporate that into your answer?
  • phk
    phk almost 7 years
    The Chrome bug has been fixed in the mean time.
  • BobTheBuilder
    BobTheBuilder over 6 years
    In order to support Firefox I've also added "ondragexit" listener to decrement counter if not 0.
  • Woody
    Woody over 6 years
    @BobTheBuilder, @B T can you suggest an edit and I'll add it to the example ?
  • Josh Bleecher Snyder
    Josh Bleecher Snyder over 6 years
    Does this work for "children" that are css pseudo-elements or pseudo-classes? I couldn't get it to, but maybe I was doing it wrong.
  • Josh Bleecher Snyder
    Josh Bleecher Snyder over 6 years
    To get this to work reliably, I had to (a) store the counter per-element rather than globally and (b) abandon the old counters when a new drag began. To do this, I used a global generation counter var dragRefCountGen = 0;, increment it in dragstart: dragRefCountGen++;, and reset the counter in dragenter when we're in a new drag: if (this.dragRefCountGen != dragRefCountGen) { this.dragRefCount = 0; this.dragRefCountGen = dragRefCountGen; }. From there, as before--increment this.dragRefCount in dragenter, decrement and check this.dragRefCount in dragleave.
  • Josh Bleecher Snyder
    Josh Bleecher Snyder over 6 years
    @Woody happy to suggest this as an edit if you'd like.
  • broc.seib
    broc.seib over 6 years
    Excellent answer.
  • Francesco Marchetti-Stasi
    Francesco Marchetti-Stasi over 6 years
    Looks like the most simple solution to me, and it solved my problem. Thanks!
  • Alan Deep
    Alan Deep about 6 years
    this is the best solution so far, just assign an id to the parent element (e.g "parent_element") and in the css file #parent_element * {pointer-events: none;}
  • Zoltán Süle
    Zoltán Süle over 5 years
    it works only if you do not have other controller elements (edit, delete) inside the area, because this solution blocks them too..
  • Scaramouche
    Scaramouche over 5 years
    I had this same problem and your solution works great. Tip for others, this was my case: if the child element you're trying to make ignore the drag event is the clone of the element being dragged (trying to achieve a visual preview), you can use this inside dragstart when creating the clone: dragged = event.target;clone = dragged.cloneNode();clone.style.pointerEvents = 'none';
  • le hollandais volant
    le hollandais volant over 5 years
    This solutions works nice when the actual mask is the #drop::before or ::after. Also, beware that sometimes, when dragging fast, the "dragleave" fires before the "dragenter" has finished. If the dragenter add the class/pseudoelement, and the dragleave removes then, this can cause problem.
  • Chin
    Chin about 5 years
    @JoshBleecherSnyder can you post the full code as an answer please
  • Adam Taylor
    Adam Taylor about 5 years
    This doesn't seem to work in Safari 12.1: jsfiddle.net/6d0qc87m
  • Max
    Max about 5 years
    Nice solution but it does not work properly if e.currentTarget outlines is not a rectangle.
  • Matt Leonowicz
    Matt Leonowicz about 5 years
    This solution is not very reliable, because when you drag things fast, not all the elements in the tree will actually fire the event, which might result in the sum different then 0 after dragging in and out. This happens more often if you have a lot of other elements in the dom tree between the most external and the most internal element.
  • Tim Gerhard
    Tim Gerhard almost 5 years
    Thanks buddy, i like the plain javascript approach.
  • Peter Moore
    Peter Moore over 4 years
    @MattLeonowicz I tried this solution with very complex elements and your concern did not manifest.
  • Jay Dadhania
    Jay Dadhania over 4 years
    When using on elements having border-radius, moving the pointer close to the corner would actually leave the element, but this code would still think we are inside (we have left the element but we are still in the bounding rectangle). Then the dragleave event handler won't be called at all.
  • Jay Dadhania
    Jay Dadhania over 4 years
    I agree with @H.D. Also, this will cause problems when element has large border-radius as I explained in my comment on @azlar's answer above.
  • Bersan
    Bersan over 4 years
    @Pipo What is event.currentTarget, is this a thing? I see explicitOriginalTarget, originalTarget, relatedTarget, but no currentTarget, at least on Firefox.
  • antoni
    antoni over 4 years
    This is great! thanks for sharing @kenneth, you saved my day! A lot of the other answers only apply if you have a single droppable zone.
  • Alex
    Alex about 4 years
    As of March 2020 I've had the best luck with this solution as well. Note: some linters may complain about the use of == over ===. You are comparing the event target object references, so === works fine too.
  • iwis
    iwis about 4 years
    For me, it works in Chrome and Firefox, but id doesn't work in Edge. Please see: jsfiddle.net/iwiss/t9pv24jo
  • iwis
    iwis about 4 years
    I use even simpler modification of @Christof code: if (e.x <= rect.left || e.x >= rect.right || e.y <= rect.top || e.y >= rect.bottom)
  • Konstantin Kozirev
    Konstantin Kozirev about 4 years
    This should be an accepted answer. Or answer in the top.
  • sudoqux
    sudoqux almost 4 years
    For interactive child elements inside the drop target, such as a button: Add pointer-events: none; to a class. Apply the class to the drop target, using ondragenter. Then remove the class from the drop target in ondragleave.
  • Tod
    Tod over 3 years
    Following on from le hollandais volant's comment, I was STILL getting issues with flashing due to dropleave and dropenter even with Pointer-events: none . So I basically set a timeout on dropleave that's cleared by dropenter. Sorted.
  • sw1337
    sw1337 over 3 years
    @sudoqux You mean apply the task the children of the drop target using .droptarget *
  • leosok
    leosok over 3 years
    Using dropzone.js a similiar aproach worked for me: console.log(e.clientX + "/" + e.clientY ); if (e.clientX == 0 && e.clientY == 0 ) { console.log('REAL leave'); }
  • Rob
    Rob over 3 years
    Nice, simple solution. Folks may want to consider just creating a class, .no-pointer-events {pointer-events: none;} then add no-pointer-events to each child element. Done.
  • tlorens
    tlorens over 3 years
    This answer is like magic! It just works. Used this method for DropZone and a HTML table for highlighting rows (The dropzones) for file uploads.
  • Ambrus Tóth
    Ambrus Tóth about 3 years
    This should be the accepted answer, it's the most simple and there are no hacks involved. :D
  • Robo Robok
    Robo Robok almost 3 years
    But I want to keep my child elements interactive :(
  • mar10
    mar10 almost 3 years
    I tried this solution, but it should be noted it has a limitation: when pressing Esc to cancel the drag&drop action, no one will remove the "over" class from your dropzone element. If you are trying to use "dragleave" event for this, you just go to the first problem the original author asked about. So both hovering a child element and pressing Esc key while dragging will raise "dragleave" event on the dropzone. Maybe we also need to listen for the Esc key to remove the "over" class from the dropzone...
  • Abubakar Azeem
    Abubakar Azeem almost 3 years
    hi @mar10, thanks for pointing this issue, i will update my answer. i think we can use dragend event to handle this, but i will need to test that.
  • Abubakar Azeem
    Abubakar Azeem almost 3 years
    I haven't found a way to detect if the dragend event is triggered by releasing a mouse button or pressing the Esc key, so going with the dragend event will complicate the logic, so the simple solution is to detect the Escape key as you said. I've updated the answer.
  • IWonderWhatThisAPIDoes
    IWonderWhatThisAPIDoes almost 3 years
    Welcome to Stack Overflow! Make sure to take the tour as it should point you in the right direction. I'd also like to ask You not to use nonpermanent indicators to refer to a post (Could You please fix that one yourself? Even now I'm not certain which answers You were refering to).
  • Miguel
    Miguel almost 3 years
    This worked for me, both Chrome and Firefox (as of 92.0). A workaround for the disadvantages is to toggle this CSS conditionally to it being dragged over.
  • Ayyash
    Ayyash over 2 years
    but what if the child component is a link?
  • nephiw
    nephiw over 2 years
    #box must be position relative for this to work correctly; but I was able to get this solution working very cleanly. Thanks!
  • Jack_Hu
    Jack_Hu about 2 years
    This is ALMOST perfect... With one exception: If the children are inputs of type checkbox or radio, then pointer-events: none will have no effect. This is because pointer-events are only those that result in a change of VALUE on an element. In the case of checkbox and radio, these change their STATE, not their value.
  • Justin K
    Justin K about 2 years
    Excellent solution - using React was as simple as tracking a new state to reference onDragEnter and check against in onDragLeave. This should be the accepted answer.
  • Peter Moore
    Peter Moore about 2 years
    This solution is brilliant and works great on modern browsers. No one cares about IE. Not sure why there seem to be so many detractors. If you are doing proper component state management almost all the criticisms become moot. And to echo others - don't forget to reset on drop!
  • sdvnksv
    sdvnksv almost 2 years
    Looks like the only viable solution among those listed. In React don't forget to store the variable in a useRef hook to make sure it's not reset on rerender (also don't forget to reset it onDrop as @PeterMoore suggested!