
The HTML5 drag and drop disaster (2009) - striking
http://www.quirksmode.org/blog/archives/2009/09/the_html5_drag.html
======
couchand
Just built a pretty significant business application with a UI based on HTML5
drag and drop. There were certainly a few quirks, but it was nothing compared
to web development in "the old days". Without any library the whole DnD song
and dance is implemented as part of a React component that's only about 100
lines of JavaScript.

There are two places likely to trip you up. One of them is as mentioned in the
original article: it's hard to remember which events need to have their
default prevented. At the moment we're calling `preventDefault` in
`onDragOver` which is required to identify the element as a drop target, and
`stopPropagation` in `onDrop` which I believe is to prevent many browsers'
default attempts to navigate to a dropped url-like thing.

The other potential pitfall is that, for security reasons, the data can only
be read in `onDrop`, not in `onDragOver`. If you have a string id you don't
mind being downcased you can just use the hack of sending that through the
data transfer types (eg. as type "id/12345"), with a "text/plain" fallback.

------
JoshTriplett
> There are no less than seven events associated with drag and drop:
> dragstart, drag, dragover, dragenter, dragleave, drop, and dragend.

> This seems rather a lot for a series of actions that can be accurately
> described by the mousedown, mousemove, and mouseup events.

Mouse events also include mouseover, mouseout, mouseenter, and mouseleave.

The start/over/enter/leave events serve useful purposes, notably that you can
offer UI cues to the user. For instance, if you drag an image onto an image
search page that normally accepts text, the search box can become a giant drop
target with "drop image here to search" text. And if you drag a non-image onto
the same page, the page can check the MIME type and give the user UI that says
that won't work.

~~~
Gracana
The author gets into that in "dragthat and dragsomethingelse," where they talk
about the problem with dragenter and dragleave behaving like mouseover and
mouseout, events which fire many times in situations where you generally don't
need them to.

------
romaniv
I still don't get why there it no baseline HTML-only drag-and-drop
functionality. It would cover a lot of cases with zero effort from developers:

    
    
      <dragarea group="groupX">
        <draggable value="abc">
        whatever you want here
        </draggable>
      </dragarea>
    
      <dragarea group="groupX" name="variableName">
      </dragarea>
    

You drag element from first dragarea and get variableName=abc on the server.
Multiple values work same was as checkboxes.

Fancy events could be added incrementally, later.

------
InfiniteRand
Honestly, if I were still a web developer, I would still use a library. The
libraries greatly simplify things for the standard use cases.

If I could not use a library, I would look for the unminified code of a
library and copy and paste it the relevant section.

Because ultimately, unless things go wrong and I need to dig deep, I have
better things to do than mastering yet another complex web api.

~~~
Someone1234
Nothing you said is incorrect, nor do I behave differently (who doesn't
utilise JQuery?).

That being said; it seems like a copout to ignore glaring issues while waving
our collective hands in the air and saying "just use a library to abstract
it!"

One of the reasons libraries are as popular as they are (particularly JQuery
which does "nothing" except provide an abstraction) is because the standards
are so weak and cross browser support so incomplete.

A lot of people think we're currently in a great place in terms of web-
development. I actually disagree. We're still in a bad place, it just so
happens that were were in an even worse place previously (e.g. IE6 era).

Is there any real motive in the standardisation community or web community in
general to make the spec' good enough so JQuery is no longer required by 9/10
websites? Why isn't $('#something').click(); built in yet?

~~~
untog

        document.getElementById('something').addEventListener('click',function() {})
    

works on all browsers these days. But we're stuck in our habits.

I don't deny that web development is kind of crappy, but it's just in the
nature of it - the reason why iOS and Android are easier to develop for is
because there's really only one client for them. Personally, I'll take the
multiple, open client approach over the closed one any day, warts and all.

~~~
bshimmin
One key difference is the jQuery version will gracefully do nothing if
"#something" doesn't exist, while yours will throw "TypeError: Cannot read
property 'addEventListener' of null".

~~~
NhanH
Additionally, that's actually not equivalent to Jquery .on(), which is the
recommended way to add event nowadays.

------
ganarajpr
If someone was using angular and wanted to use the native html5 drag and drop,
I created a directive for it here [https://github.com/ganarajpr/angular-
dragdrop](https://github.com/ganarajpr/angular-dragdrop) if you are
interested.

There is documentation as well in here : [http://ganarajpr.github.io/angular-
dragdrop/](http://ganarajpr.github.io/angular-dragdrop/)

Having read the blog ( from top to end ) I kind of agree with some of the
things that the author says. There are a few edge cases that we need to take
care of if using native drag and drop. There is also the not-so-slight issue
that it doesnt work on mobile browsers....

------
fiatjaf
What about this[1] non-jQuery "old script"? Can we use it instead?

[1]: [http://draggabilly.desandro.com/](http://draggabilly.desandro.com/)

~~~
striking
This may be precisely what I, personally, needed for my web-app. I'll try it
tonight and see how it works out, thanks for the link.

------
panzi
About prevent default: You need to tell the browser that _this_ element will
handle the drop/has handled the drop, or it will perform its default
operation. What is it's default operation? To load the file/URL that you just
have dropped onto the browser window.

Or maybe you have multiple drop areas stacked on each other. Should the event
propagate to the parent element or not?

Ok, why is it done in JavaScript and not simply with dropzone="true"? Because
you want to be able to let script logic decide if that drop/dragover event is
eaten or not.

I stopped reading at some point, but I wonder if the author has ever
implemented D'n'D support in any other language. It works very similar to this
in I guess most GUI APIs. Maybe that is another reason why it works like that
in HTML5?

~~~
eridius
I sympathize with your argument, but unfortunately it doesn't seem to be
correct.

Arguably, you should be able to implement just the 'drop' event and call
event.preventDefault() in that event to suppress the default behavior of
loading the file/URL. I don't see a clear reason why any other event should
matter, the actual action that triggers the load is dropping.

As for propagation to parent elements, that's not controlled by
event.preventDefault(), that's controlled by event.stopPropagation().

I do agree that the author doesn't seem to realize that drag & drop is
actually rather complex and that other languages look similar (e.g. large
number of events). But the quirks around when you have to call
preventDefault() do seem to be legitimate issues.

~~~
panzi
Yeah, in other languages you don't use "preventDefault" but e.g.
"acceptProposedAction". So they could have added such an method to the event
object. I guess it's a bit of a quick fix thing why Microsoft didn't do that
and the w3/whatwg just took that.

As to "I don't see a clear reason why any other event should matter". Well,
yeah, one could implement it so that only the drop event really matters, but I
guess it's done the way it is so it forces the programmer to explicitly handle
the dragover event. This way the appropriate mouse icon will be displayed and
the user knows what's going to happen when they drop now.

Strange situation where I, a Linux user, defend something made by Microsoft.
Usually I spew a _lot_ of hate against Microsoft just for committing the crime
called Internet Explorer (especially version 8, which I still have to support,
bah).

------
iza
It really should only be used to drag and drop files from another application.
If you just want to move elements around on the page mousedown, mousemove, and
mouseup are all you need. I don't think we need a new API for this as it is
pretty straightforward to roll your own.

~~~
striking
But future devs shouldn't have to "roll their own" when someone's done it
before. Why not just rewrite the standard and make it sane? JQuery shouldn't
be the de facto JS library, those features should be (and are being)
integrated into HTML already.

------
aniketpant
One of the best and well explained issues with the HTML5 DnD.

The specs for DnD are written very well but the implementation is brilliantly
fucked up that the only way you are left is to use a library and enjoy the
hardwork of someone who spent hours getting smeared in the dirt.

------
mraison
_> Web developers MUST NOT (in the sense of RFC 2119) use HTML 5 drag and
drop. They should use old-school scripts instead._

As far as I remember if you do that (i.e. if you use mousedown, mouseover, etc
and move your dragged element accordingly), the result won't be nearly as good
as with "native" drag and drop, and you will see the dragged element lagging
behind the cursor.

~~~
ascotan
I've implemented drag and drop multiple times via jquery event handling and
never had lagging issues.

~~~
panzi
Indeed. I also implemented it by hand once and once I did a bit of event
throttling (which jQuery also does) it worked smoothly enough even on a very
old Android 3 tablet (in Firefox). And the dragged element has opacity!

------
dm2
@striking: Does this work for you?

[http://www.html5rocks.com/en/tutorials/dnd/basics/](http://www.html5rocks.com/en/tutorials/dnd/basics/)

[http://html5demos.com/drag](http://html5demos.com/drag)

I don't understand why this is an issue, there are tons of great tutorials:
[https://www.google.com/search?q=HTML5+drag-and-
drop](https://www.google.com/search?q=HTML5+drag-and-drop)

Step 1 to being a web developer, search Google first. More than likely
anything you need done has been done 10,000 times and there are at least 100
tutorials.

Don't be afraid to use libraries such as jQuery,which HTML5 drag-and-drop
doesn't require, but I see that you mentioned avoiding it in another comment.
Libraries many times also have the advantage of increasing browser support
transparently, which is generally a good thing.

Worried about loading time with libraries? Use a CDN such as
[http://cdnjs.com/](http://cdnjs.com/), most users will have the popular ones
already cached in their browser.

Use StackOverflow if you have questions, that's what it's there for. Hacker
News is for... news.

~~~
striking
* I looked into those, but wow wow wee wow are they complex. I'll be working with cloning/moving DIVs instead, especially because HTML5 isn't supported everywhere.

* I prefer to avoid libraries when possible, especially when diametrically opposed ones are in use (I'm making an Angular app, JQuery doesn't always play nice).

* Thank you for the tip about [http://cdnjs.com/](http://cdnjs.com/), I'll be using it in the future.

* Hacker News is news, but it's also about discussion. That and I've personally never heard of how bad HTML5 DnD was, I thought the community might benefit from looking into it too.

~~~
brianbarker
Since when do Angular and JQuery not play nicely? Angular uses "JQLite" which
gets replaced with JQuery if it's already loaded.

I guess if you're trying direct dom manipulation on the same elements from
outside of Angular with JQuery and with Angular it could get messy. The
community would just say "you're not doing it the Angular way." You're not.
You can use the JQuery inside a directive and controller and be fine.

[https://docs.angularjs.org/misc/faq](https://docs.angularjs.org/misc/faq)

~~~
Macha
Ok, to give a specific example:

We use drag and drop for sorting in a few places. Our initial implementation
was to wrap jQuery UI Sortable in a directive. This worked to an extent, but
for our purpose, we ended up having to read the DOM and update the Angular
scope from the DOM after jqUI emitted its event. This logic ended up being
somewhat clunky.

We rewrote our directive in terms of HTML5 drag and drop such that when a user
drops something, we get an event on the Angular scope, saying which element
was dragged, and which element it was dropped on and then our controller
rearranges the array in the scope, and only then is the DOM modified by
Angular.

This generally isn't possible with jqUI and its plugins, which modify the DOM
first and tell you about it later (in such unhelpful low level terms as "moved
100px up" rather than "moved two elements forward").

~~~
brianbarker
Yeah I can see how that would be a problem. Maybe it'd be better to clarify
that JQuery and Angular work great together; however, JQuery _plugins_ (which
may have all kinds of selectors, state, DOM manipulation) cannot be guaranteed
to do what you want inside of Angular.

------
mereskin
I made an overview of advanced drag-and-drop features and their support in
browsers (with examples):

[http://mereskin.github.io/dnd/](http://mereskin.github.io/dnd/)

------
chiubaka
I didn't read that closely enough to understand exactly what was being said,
but it was fucking hilarious.

------
moron4hire
I've seen this naming pattern a couple of times in articles submitted to HN.
The "... considered harmful" pattern starts with Dijkstra, "GOTO statement
considered harmful"[1]. It doesn't mean "such-and-such feature is implemented
incorrectly." It means, "such-and-such feature, even when implemented
correctly, gives programmers at large the wrong notions."

So, one would say "singletons considered harmful". The entire notion of
singletons is wrong. But the entire notion of drag/drop events is not wrong.
Just the implementation.

[1] It's interesting to read some of the counter-arguments, and reflect on
programming language design arguments we have today:
[http://web.archive.org/web/20090320002214/http://www.ecn.pur...](http://web.archive.org/web/20090320002214/http://www.ecn.purdue.edu/ParaMount/papers/rubin87goto.pdf),
and Dijkstra's own commentary on the whole ordeal is--as usual--entertaining
[http://www.cs.utexas.edu/users/EWD/ewd10xx/EWD1009.PDF](http://www.cs.utexas.edu/users/EWD/ewd10xx/EWD1009.PDF)

~~~
jackweirdy
> The entire notion of singletons is wrong

Could you expand on that? Haven't heard this argument before

~~~
cpitman
They are generally misapplied by developers as a sort of global state, usually
with the implied belief that "I'll only ever need one of these".

This has all kinds of nasty downstream effects. It becomes more difficult to
trace the dependencies between classes, since any class can invisibly depend
on a singleton. It becomes painful/impossible to replace that dependency in a
particular instance, which makes both testing and extension difficult.

Dependency injection solves the issue of sharing copies of the same object,
without forcing that the object is implemented as a singleton. You can inject
a different/extended implementation, and it becomes easy to trace
dependencies.

~~~
jimmaswell
How does it make it harder to trace class dependencies? Find all references to
the singleton.

Sometimes you just really need some global state. Have you ever done game
development? Unless you want to pass a giant GameState godobject to every
single thing. Singletons or static classes:
NotificationManager.PushNotification(..),
DBManager.Instance.UpdatePosition(..)

Singletons are good for when you want a static class but also need some things
you can only do with instance classes.

~~~
moron4hire
I've actually done a lot of "game" development, both as a hobby and in non-
game-development fields (lite simulation work increasingly resembles games,
and I think in general they should be developed in the same way).

Static classes are a hack for languages that force everything to be an object.
You don't see static classes in languages like C++ or Common Lisp or
JavaScript. You pretty much only see it in Java or C#. You make global (though
certainly namespaced, we don't need to be colliding names here) functions
instead.

And the difference between instance classes and static classes is that
instance classes encapsulate state. So a singleton is literally nothing more
than stateful global functions.

The realization you're starting to need a GameState godobject is what is
called a code-smell, i.e. trying to find answers to the wrong problem of "how
do we get all of this state around?" The problem should more correctly be "how
do we avoid needing to have so much state passed around?"

It's usually a sign that the project is using too much inheritance. It seems
natural to have a class "ProjectileWeapon" that has a virtual "fire()" method
that returns a "Projectile", from which "Gun" inherits and overrides fire() to
return a "Bullet" and "RocketLauncher" overrides to return "Rocket". But the
Bullet only has a momentum vector, the Rocket also has fuel, and the Bullet
only does kinetic damage, the Rocket includes chemical, and different
materials are more or less resistant to different types of damage. The
interactions between types that all try to encapsulate their own behavior
starts to multiple the number of cases where different bits of code need to
know about different parts of the world.

It at first seems to reflect the real world, but in reality a Rocket knows
nothing about the air it is flying in and the objects it hits and blows up on.
And those objects don't know anything about the Rocket, either, they are
equally a'splodey whether they're hit by a Rocket or a Grenade.

The better way is Composition, the "has-a" in "is-a" versus "has-a". A Rocket
has fuel. You don't say a rocket "is a fueled thing". A physics engine takes
all of the things that have fuel and the things that require fuel (rocket
engine) and asks "what is your fuel flow rate" and "what is your fuel
consumption rate", respectively. Interfaces and Mix-Ins can be exploited to do
this in a type-safe way.

Inheritance is useful when a strict tree-structure can appropriately
encapsulate your needs. Unfortunately, very few things are strictly tree-like.
The only times I've found a strictly tree-structured problem in the last 15
years has always involved processing language grammars. Inheritance works fine
there. But even still, Haskell-style pattern matching works better. I won't go
so far to say "inheritance is useless", but I will say my personal experience
has been that I've learned to regret having used it in every instance.

~~~
jimmaswell
>The problem should more correctly be "how do we avoid needing to have so much
state passed around?"

I work on a large game, and multiple smaller games of my own, which all use a
few static classes or singletons. It's never caused an issue, and I've never
had such a problem with inheritance. I've never regretted using it in my code.
What about Exceptions?

You don't seem to have offered a way to avoid having so much state to pass
around. Where does the notification queue go? Where do the settings go? Where
does the scene manager go? All the MVC controllers I have are static or
singletons.

I don't see what objects have to do with no global variables or functions
being allowed. There's no theoretical reason a global function can't operate
on or return objects.

~~~
moron4hire
>> There's no theoretical reason a global function can't operate on or return
objects.

That's not the same thing as stateful at all. "function add(a, b) { return a +
b; }" operates on two objects and returns an object, but it maintains no
state. I said global state was a bad thing, not global functionality. I added
a caveat that global functionality should at least be namespaced, though.

You avoid passing state around by either injecting the dependencies or
returning the state transitions to some other thing that makes the state
changes happen. You make explicit the relationships that the singletons hide.

------
onion2k
This is from 5 years ago.

~~~
aikah
what he says is still valid,the spec hasnt changed,is broken even or recent
browsers such as Chrome ,is complicated and confusing.It's another exemple of
half baked spec written by people that are never going to use it,just like app
cache.

Frankly HTML5 is a disaster(and no , things like WebWorkers or WebSockets are
not part of the HTML5 spec before anyone jumps on me).

It's just hard to build something robust on weak foundations,but that's lot of
client side developpers, making these things work even though specs are just
not helping.

~~~
thristian
I don't think it's fair to blame the HTML5 drag and drop API entirely on
WHATWG; my understanding is that it was originally specced years ago as the
sanest useful subset of IE6's drag and drop API. It may not be the prettiest
thing ever, but it's prettier than how it started out.

~~~
panzi
Subset? Isn't it more like a super set? IE6 (and above) has no files array
that can be read with a FileReader and it only supports the types "Text" and
"URL", no support for proper mime types.

~~~
thristian
I think this is what I was remembering:

[http://ln.hixie.ch/?start=1115899732&count=1](http://ln.hixie.ch/?start=1115899732&count=1)

...which is to say, IE6 supported more event types and more configurable
behaviour than HTML5 supports, but some of those extra features were just too
weird, or outright crashed IE, so they didn't make it into the spec.

------
shangxiao
Ha I remember this article, it's from a few years ago and is when I started to
lose my respect for PPK due to all the cursing and carrying on.

~~~
striking
I agree, his language is incredibly inflammatory and generally unnecessary.
But he's done a really good job of writing up the flaws, so I suppose the
tradeoff in this case was worth it.

