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.
> 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.
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.
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?
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.
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".
You say jQuery "gracefully does nothing", I say the code fails silently and the application breaks anyway with no real indication of where the problem lies.
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 if you are interested.
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....
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?
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.
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).
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.
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.
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.
> 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.
That and sharing state with other windows is nearly impossible whereas HTML5 DnD makes multi-window stuff seamless. But I agree when he says that, as it is, HTML5 DnD is ridiculously difficult to implement properly.
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!
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/, 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.
* 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/, 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.
All of the events are needed because developers need that granular control.
If you want simple, you're going to have to use a library, what you're basically asking for is for the HTML5 spec to emulate jQuery and cut out the options that you specifically don't need, which isn't reasonable.
If you have a solution to the HTML5 spec to simplify it then please write an article and we'll support it if it's reasonable and do our best to get it added to the spec (it won't be implemented into browsers for a few years though). Remember you have to support lots of use-cases, touchscreens, old browser support (unfortunate reality).
The only real complaint in the original linked-to article is about having too many events (they're not all required to be used), then it says that you have to add some CSS for it to work properly in Safari (sounds like Safari's problem, not the HTML5 spec's fault).
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.
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").
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.
jquery and angular do not play nicely not matter what the angular people say. Any non-trivial jquery plugin included into a angularjs page will require $timeout(0) hacks to get it to behave properly.
But again, is your plugin being called from an angular module, or are you running them separately on the same nodes? That's typically the problem, and it makes sense. If any two frameworks are messing with the same page content, you're bound to see issues.
Edit: see comment above as well about JQuery plugins.
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.
A pattern which really reached its apex over at Ward's Wiki [0]. It really should be reserved for concepts generally broken. I'm pretty sure I saw somewhere recently an article about "...considered harmful."
Edit: Ah, yes, Eric Meyer [1], complaining about "...considered harmful" seven years prior to the original article. :-)
"Because “considered harmful” essays are, by their nature, so incendiary, they are counter-productive both in terms of encouraging open and intelligent debate..."
Excellent point. What is it about us techies that we have to always be right, and demonstrate it in the most indignant way, damn the consequences?
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.
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.
Singletons prevent you from running more than one instance of an app inside a single VM/interpreter, in those kinds of languages. They're marginally useful if you don't care about those situations, but honestly it's never been worth it to me.
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.
>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.
>> 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.
In may frameworks service layers are usually implemented as singletons to prevent the expansion of memory requirements with a lot of throughput. However, services designed like this _should_ be stateless. Basically they exist to do the heavy lifting of data transformation. There no real problem with this.
The problem comes when you mix state into a singleton. State-full singletons are basically global variables. Even if you design all your methods as transactional/synchronized/blah/blah you will run into issue where the state of the singleton isn't what you expected because it was changed via a different method/thread/blah/blah.
Basically this a school of hard knocks thing. They're great, until you have to debug it in production.
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.
You're right, but that's the reason I think all browsers have developer channels. So that developers who actually user those API's will test them, report bugs or even stir the specs towards something useful. I know that's the case at least with WebRTC.
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.
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.
...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.
> It's another exemple of half baked spec written by people that are never going to use it,just like app cache.
Appcache has ever worked flawlessly for me. IndexedDB, though, is a the feces of a dog who has eaten too much cow shit.
Why not WebSQL? Shit, even if the only implementation used is SQLite, it's better than IndexedDB which is asynchronous, can't even do a motherfucking sort, a simple enough range query or unions.
The problem was that SQL is a giant standard that nobody implements really correctly and it would blow up the HTML5 standard enormously. Also I don't think there is anything wrong with an async-only API that does potentially do disk access.
I'm fine with the async part, if there was a proper support for stuff like joins and sorts. Without this, you have to individually gather the single rows and do it in userspace code.
This is crazy enough, add async-ness to the whole mess and even with promises you end up with a shitload of spaghetti code.
Right now IndexedDB is a kids playground and nothing usable at all for any serious application.
Updated the title to reflect that, thanks. Really makes you wonder though; why hasn't HTML5 Drag and Drop been simplified yet? I personally have no idea what to do, besides adding JQuery.
I've done some drag and drop programming, but not using the HTML5 API. I may have read that article quite a few years ago, but another problem was that HTML5 was not considered ready for use all those years ago. Recently (well, within the past year or so) I have found clients' requirements have been far less strict in terms of supporting older browsers, so I have been making use of more HTML5 features. I still have not used HTML5 drag-and-drop.
The basics of drag-and-drop only take a few lines to implement, but it's important to do extensive testing to find the edge cases. I found (one year ago) there were problems with jQuery UI that stopped me from making full use of it. I wound up using it for the basic dragging - it would reliably tell me mouse move events, clicks, buttons, and the mouse position, but because the droppables system would miss some events I implemented the logic surrounding detecting where the item was being dragged. Basically, whenever the item got moved, a whole bunch of calculations would run to see what object it's positioned over or near, and it's position within whatever object it's over.
If you want to do drag-and-drop programming in the browser, I recommend you write some code that allows you to pick up a DIV and drop it elsewhere on the screen. It's not trivial to do the first time, but once you understand the patterns it becomes so much easier. The techniques are useful for other similar things, like creating resize handles.
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.
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.