5

I am trying to solve the issue of firing the dragend event immediately, when dragged outside the draggable area. For example, notice in the following video, there is about a 0.25s delay when dragging the tag outside the draggable area (when inside it, it registers immediately): https://gyazo.com/18d1afc32eb065d1f35896697ef0479e.

And here is the JSFiddle: http://jsfiddle.net/radonirinamaminiaina/zfnj5rv4/.

This question was asked about 7 years ago here: HTML5 dragend event firing immediately, but this seems to be more like a browser restriction when it was asked and I think my question is quite a bit different.

Is there a way to fire the event immediately when it is dragged outside of the draggable area? For example, in the jfiddle example, if "This div is draggable" is dragged to the upper left corner of the browser, for it to 'snap back' with zero delay?

Update from a question in a comment: Here's an example of what I'm trying to do, with a 4s video from a Pivot table in Excel: gyazo.com/3ccd1c3abd7f92d3410022a83b5c25b9. Basically, when the user drags a tag "outside of the drag zone" I want to be able to immediately remove that tag or trigger an animation showing that the tag has been removed.

David542
  • 104,438
  • 178
  • 489
  • 842
  • So to be clear, you are asking to disable the smooth transition between where you dropped it and its final position? – Kaiido May 18 '20 at 00:03
  • @Kaiido I guess the best way to describe it is by showing what I mean. Here's an example of what I'm trying to do from a Pivot table in Excel: https://gyazo.com/3ccd1c3abd7f92d3410022a83b5c25b9. Basically, when the user drags a tag "outside of the drag zone" to immediately remove that tag or trigger an animation showing the tag has been removed. – David542 May 18 '20 at 05:41
  • Why do you listen to the dragend event? You should listen for the drop event outside of that area, by delegating it to a bigger parent (e.g `document`). – Kaiido May 19 '20 at 01:43
  • Are we talking about user dragging the element outside or drag & **dropping** it outside? – Dan Macak May 24 '20 at 10:50
  • @DanMacák dropping it outside. – David542 May 24 '20 at 18:26
  • Same as @tao, I can't reproduce it on win, and I don't have a mac here. Is it the same for you across browsers- Safari, Chrome .. ? – Dan Macak May 24 '20 at 20:57
  • @DanMacák yes, also I am on a mac, to confirm. – David542 May 24 '20 at 21:17
  • @DanMacák here's another example of how I tested (basically, it's dragging it as far out of the region as possible: https://gyazo.com/3d998e8cddfffb9c59d2ce823f26e96e. – David542 May 24 '20 at 21:18

3 Answers3

6

This was the answer for me; not sure why, but it worked!

document.addEventListener("dragover", function( event ) {
    event.preventDefault();
}, false);
Ben Robinson
  • 1,602
  • 3
  • 16
  • 30
3

What you describe is not consistent with testing, in both Chrome and Firefox, across Linux and Windows (desktop) systems. Because of the COVID situation, I don't have easy access to other browser + OS combos (I'm not exactly an Apple fan), but here's a fiddle I made where you could test any other such combo:

https://jsfiddle.net/websiter/9d7cfbmx/embedded/result/#Result

How it tests: it measures the difference in milliseconds between drop and dragend events. It also stores each of them into an array providing min, max and avg values of currently stored cases. As you can see, the difference varies in between 0.15ms and 1.75ms with average values of ~0.5ms.

Because it's a practical prototyping tool, I used Vue to update/display the stats, but that should not interfere with the events being measured at all (you'll notice they all happen and are measured outside of Vue and the data update happens in a setTimeout(), to make sure I'm not interfering with the test at all).

Unfortunately, Firefox rounds performance.now() values to 1ms, so it doesn't give you sub-millisecond min and max values, but average values seem consistent with the ones in Chrome (actually slightly smaller in my tests).

The above suggest your premise there is a 250ms delay on the dragend event is not accurate, unless both the events are delayed by the same amount of time. If that was the case, it would be visible. A quarter of a second is noticeable by human eye.

I went ahead and added a visual tester for 250ms to the above fiddle.


Another note: Looking at your demo video, you seem to be using an Apple device but it gives no clues on browser used. If I had to guess I'd say it's Safari.

Two conditions have to be met for that animation to be disabled:

  • you have to call preventDefault() on dragover event (you already do)
  • drop event needs to have been fired by the time dragend is (according to this answer)

In order for the second condition to happen, you could dispatch drop in dragleave if the event's related target is <html>. I really don't see another option:

document.addEventListener("dragleave", function(event) {
  if (event.relatedTarget.tagName === 'HTML') {
    document.dispatchEvent(new Event('drop'));
  }
})

Note: by calling drop when dragging out of <html> you're breaking any case of dragging stuff out of the browser into any other programs (which, AFAIK, is one the intended uses of D&D). Also, to make sure you're only doing the dispatch when actually dragging out of the viewport, you should add this CSS bit: body { min-height: 100vh; }.

Also, as already mentioned, I have no way of testing on a Mac now, so I don't guarantee this hack works. It should, but... it's an Apple implementation, you know? Let's just say with Apple anything is possible. Maybe because it's been bitten, who knows? :)

You can test the hack here.


If the above doesn't hide the animation, this might. Look into dragend for details. (It's an attempt to not fire drop beforehand (out of principle), but rather fire it from dragend and re-dispatch a cancelable dragend mock). IMHO, worth a shot.

tao
  • 82,996
  • 16
  • 114
  • 150
  • thanks for this comprehensive answer. I just added a further video in the comment if that's helpful (and yes, it's on mac). – David542 May 24 '20 at 22:14
0

You can try to fire the dragend event yourself when dropping an element like that :

document.addEventListener("drop", function( event ) {
    event.preventDefault();

    // currentTarget refers to document because I added the listener on it.
    event.currentTarget.dispatchEvent(new Event('dragend'));
}, false);

I can't test if this will remove the animation because it seems to be a behaviour that is caused by MacOS?

Also notice that this may make the event dragend fire twice.

I hope this will help!

Draityalan
  • 710
  • 1
  • 4
  • 15