Anatomy of a Drag and Drop
The mechanics of a drag and drop are pretty interesting and most library implementations do similar things, albeit to various extents. As I explain the inner workings, I'll touch on Yahoo's, Mootools', and Script.aculo.us' implementations, what to expect, and how they differ.
A drag and drop essentially consists of a few events (and by events, I mean, moments in time, not DOM events). There's the moment a drag is started, there's the continuous dragging of an item, and finally there's the moment the item is no longer being dragged (ie: the drop). The items being dragged are sometimes referred to as draggables.
On top of these events, there is also the interaction with other elements on the page; dragging over and out of an element and whether the dragged element has been dropped on top of another element on the page. The elements upon which draggable items are dropped onto are sometimes referred to as droppables. I'll cover droppables in the next part. For now, let's go over the draggables.
Starting the Drag
In a drag and drop, we need to know when to start the drag. This is done by attaching a mousedown
event on an object. Normally, this is attached to the object that we want to drag.
Alternatively, this could be attached to a drag handle. A drag handle is an element other than the container itself that should initiate the drag. For example, if you had a "panel" and you only wanted the title bar to initiate the drag then the title bar would be your drag handle. None of the implementations stop you from specifying a drag handle that exists outside of the draggable. (Not that I'm saying they should stop you but it's interesting to note and maybe there are some cool things you could do with this.)
- Mootools requires an element reference or an element id to be passed in for the draggable and its handle. The element to be dragged also needs to have its position explicitly set to relative or absolute.
- Script.aculo.us can take an element reference or an element id for the draggable. For the handle, you can also specify a class name. It will try and locate the first item within the draggable that has that class name. If it can't find it, it'll try and find an object on the page with that id. Script.aculo.us will also take an element reference.
- YUI takes just an id (not an element reference) but allows you to specify as many drag handles as you'd like for a single draggable object.
With the item that we want to drag, a mousedown
event is attached to the handle (or the draggable itself, if no handle was specified).
- Mootools (revision 83) is fairly obtrusive in that it overwrites the
mousedown
event of the draggable upon initialization (that is, any applied likeelement.onmousedown = function
).
Dragging
Once the mousedown
event is fired, the gears of the drag are set in motion. First, by applying mouseup
and mousemove
onto the document. The mousemove
event fires as we continue to move the mouse around the page. Each time the event fires, the X and Y values of where the mouse is on the page are captured. The mouseup
event determines when we've completed the drag.
- YUI allows you to specify a drag threshold. This value requires the user to move the mouse a certain number of pixels before the drag actually begins.
Each of the libraries will make a callback — onStart
in Script.aculo.us and Mootools, and startDrag
in YUI. This allows us to prepare the drag element or other elements on the page for this event. Script.aculo.us gives us an extra callback called starteffect
. It's intended to apply an effect to the element being dragged, such as changing the opacity or other effect.
- Mootools, like on the element's
mousedown
event, is fairly obtrusive in that it overwrites themouseup
andmousemove
events of thedocument
. If you're using addEventListener (or their equivalents) then you should still be okay.
As the mousemove event fires, the X and Y values are used to reposition the draggable item around the page. This is done by calculating the difference between the last X and Y coordinates and the current coordinates. The change is then applied to the top and left properties of the element.
- Mootools lets you enable element resizing (instead of drag and drop) by changing the height and width properties of an element instead of top and left.
Each library also implements a callback function to be fired each time the mousemove event does; onDrag in YUI and Mootools, and change
in Script.aculo.us.
- Script.aculo.us has a Draggables singleton that you can register listeners with. Any time an object is dragged (or started or stopped), an onDrag callback (or onStart or onEnd, respectively) is fired for every item registered with the Draggables object.
Stopping the Drag
Once the mouseup
event fires on the document, the mouseup
and mousemove
events are removed from the document. One final callback is made; onEnd
for Script.aculo.us, endDrag
for YUI, and onComplete
for Mootools.
- Script.aculo.us will fire off two more callbacks. The first is
reverteffect
and the other isendeffect
.reverteffect
, by default, will animate the object from the drop point back to its original point. By default,endeffect
resets the opacity of the object back to 100%. Overriding the default means any functionality there before will not execute. For example, if you were to overrideendeffect
, the opacity would remain unless you manually reset it.
Example
I put together a quick example of a drag and drop implementation. This is a home brewed demonstration to show that the mechanism is fairly straightforward. Where the complexity comes in is in handling the variety of situations that might be thrown at it, and that's where libraries really have their advantage.
(For fun, I created an example that changes height and width to demonstrate element resizing.)
Next up
Creating a draggable is only a small part of the equation. Next up I'll cover the concept of a drop zone (or droppable as they're known from Script.aculo.us and Mootools) and how to determine that you're over a droppable.
Conversation
Nice article Jonathan. I'm in the middle, well, beginning really, of trying to determine which library I want to move forward with for our Admin.
I can see this series being useful at helping me get to know some internals of the libs without having to dig in too far myself* :-)
* Which I will have to do anyway, but you spelling some things out like this sure helps!
Good article! Another library you might want to consider is jQuery. The Interface plugin has components for draggables, droppables, sortables, selectables, etc. It's highly configurable and quite robust.
Thank you for this great article! It's nice to get an insight to these techniques.
I'm looking forward to more like this!
Nice article. Great to see this level of depth comparing some of the big JS libraries out there.
I find it hard to get the time to learn one major library, let alone 3. But recently I've been sticking more with YUI and have been pleased with the framework.
WooW !! Amazying simple article !! Thanks :)
nice article :)
unfortunately this method doesn't work for iframes to be embedded widthin the divs to make them draggable.
has anyone tried this using prototype library? a non-prototype solution for this problem can be found here
http://www.mattkruse.com/javascript/dragiframe/
Great Article !
I would be obliged if you can discuss how to implement draggable structures like we have in netvibes.com (you see the drag only happens in specified columns & overlapping is not allowed thus if i want to put C div in between A div and B div ...then B automatically moves down opening up space for C div)
I have been trying to implement it using Yahoo! library but have struggled so far.
Overall great stuff :)
I've been working on my own drag and drop code for an upcoming project and I see that you have a similar problem in your example -- when you drag a page element, it selects text as if you clicked and dragged across a static paragraph of text. Is there a decent way to prevent this? Google Calendars doesn't seem to do it in their model.
Thanks for the nice analysis!
I am implementing a custom library for a project of mine since this will be my first touch with dom scripting. I don't want to use prepackaged code as it will curb my learning. So this will definitely help
schello: although I haven't tried it, I imagine it wouldn't be too difficult. The key would be to write a pass-through function to handle events from the iframe.
Muneeb: I'll definitely get into more of that in the upcoming article. Thanks. :)
Brent: There's an easy way to prevent that. If the drag function is attached via an element.onmousedown event, just put
return false;
at the end of the function. I've updated the drag and drop example to demonstrate. If you attach via addEventListener then use event.preventDefault().Hey Jonathan,
On the YUI team we've been using the term "Interesting Moments" to help think about the key events in time in complex interactions such as Drag and Drop and AutoComplete. In a collaborative process with our User Experience Designers and our YUI Engineers, we've created storyboards of the interesting moments that the large numbers of new "Actors" (e.g., "valid drop target", "intertion bar", "tooltip") encounter as they cross time and stage.
The result is an "Interesting Moments Storyboard" which we offer as part of YUI to help implementers think through these more complex interactions in a thorough and rational way.
Here in the Interesting Moments Storyboard for Drag and Drop:
http://developer.yahoo.com/yui/dragdrop/#storyboard
Here's the storyboard for AutoComplete:
http://developer.yahoo.com/yui/autocomplete/#storyboard
My colleague Bill Scott has blogged on the topic a bit too:
http://looksgoodworkswell.blogspot.com/2005/12/storyboarding-interesting-moments.html
I also wanted to offer a bit more information about "drag handles". In addition to YUI's support for multiple drag handles, it also supports specifying what's /not/ a drag handle. This is useful when you want to say "the entire title bar is a drag handle /except/ the close button img and the headline link." This is accomplished with three methods in the library:
invalidHandleClasses
invalidHandleIds
invalidHandleTypes
You can read more about those methods in our API documentation:
http://developer.yahoo.com/yui/docs/YAHOO.util.DragDrop.html#invalidHandleClasses
I'd happy to see you digging deep into these libraries - you're performing a great service for the community. Thanks again for a great article.
Regards,
Nate
Nate Koechley
natek at yahoo-inc dot com
Engineer and Designer,
Yahoo! YUI Team
Nice comparison. We've saved it to share with the TekTag community. Lots of interest in this kind of stuff.
One more Snook,
Don't you have an RSS Feed for the blog? I tried to find it but could'nt.
Muneeb: the RSS feed is located in the bottom right hand corner of the page.
Muneeb:
Its also (on any modern browser) sitting in the URL bar or at the bottom-right hand of the screen OR ... at the very least it should be listed in the HTML
(just for future reference ;))
[b]Cole Mickens[/b]:
Does IE 6.0.2900.2180 count as a modern browser? :D
Just Kidding. Thanx for the tip because I just opened it in Firefox and saw the icon :)
Very interesting!
Thanks, it is really good!
Thanks so much for the drag and drop anatomy article!
I've done this many times for non-web (windows c#) applications, and now I see the similarities,
but your article helped me a lot for the javascript variant!
Thanks again!
Ronald
The Netherlands
Onde está o Codigo do DRAG DROP com IFRAME
Very interesting article!! But I was actually looking for another feature: Dragging objects from outside the browser into a page. For example dragging a file from the file explorer into a web page so it uploads it.... Do u have a hint for that?
http://www.fatpublisher.com.au/resources_demo.php?id=8
If you move the mouse too fast..
the drag is "dropped" mistakenly.
Great Article. I'm going to try to implement it on my website but I first want to know if this "drag and drop" method is W3 compliant or at least compatible with IE 5 and Mozilla 1.0. I know a lot of new, nifty, cutting-edge web tricks are not yet "standard" yet and I just want to make sure. :)
@fatpublisher: the problem is that at times the mouse will be over the other two draggable elements in the middle of the drag, freezing the animation. I'd probably look at ways for events to stop being captured on those elements while in the drag.
@hacker: these techniques *could* be made to work with older browsers but I haven't bothered to test. It's mostly DOM method compliancy.
Another draggable/resizeable iframe demo:
http://cross-browser.com/x/examples/xiframe.html
great article btw :-)
Great looking site! I love the way you have employed elements that visually jump out of the container (i.e. your flags) as well as your color schemes (particularly the stylizations of graphical elements and the way your comment numbers bleed into the banner strip below it... Well done! =)
Only thing though, how do I navigate to the next installment of this article?
Hi, I was wondering if you could help me set up my own javascript focus thing like you have on this comment form.
For instance... When you first look at the field 'name' its value is 'Name' but when you click in the field its value goes blank, yet if you keep it blank and click somewhere else the original value returns...
I would greatly appreciate your help on this as I would like to integrate this sweet feature into my own input box.
Thanks,
-Blake
Nice article Snook. Did you get around to writing the second article on what muneeb requested?
Old, but very fast. Have you any more articles around this?
To Blake: read this : Autopopulating text input fields with JavaScript
I should have properly linked this up but this was the follow-up: Determining the Droppable
When attaching a mousemove event to the document, the event object target's element that fireup the mousemove is different in Firefox than in IE and Safari.
In fact in Firefox it is the element under the absolute element that follow the mouse (the draggable) and in IE and Safari, it is always the element directly under the cursor which is the draggable element (which is a better behavior I think).
But how can I have access to the element under the element under the cursor?
I use no plugin.
Thanks.
Could you help me. The greatest thing in the world is to know how to belong to oneself.
I am from Iran and learning to write in English, please tell me whether I wrote the following sentence: "The book I write appears on the album music from the motion picture stranger than fiction."
With respect :), Cato.