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 like element.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 the mouseup and mousemove events of the document. 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 is endeffect. 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 override endeffect, 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.

Digg it?

Published November 17, 2006
Categorized as JavaScript
Short URL: https://snook.ca/s/723

Conversation

33 Comments · RSS feed
Mike Papageorge said on November 18, 2006

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!

Karl said on November 18, 2006

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.

Julian Schrader said on November 19, 2006

Thank you for this great article! It's nice to get an insight to these techniques.

I'm looking forward to more like this!

Aaron Schmidt said on November 19, 2006

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.

Balakumar Muthu said on November 20, 2006

WooW !! Amazying simple article !! Thanks :)

schello said on November 20, 2006

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/

Muneeb said on November 20, 2006

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 :)

Brent Traut said on November 21, 2006

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.

Nikhil said on November 21, 2006

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

Jonathan Snook said on November 21, 2006

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().

Nate Koechley said on November 21, 2006

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

Bluedog said on November 21, 2006

Nice comparison. We've saved it to share with the TekTag community. Lots of interest in this kind of stuff.

Muneeb said on November 23, 2006

One more Snook,
Don't you have an RSS Feed for the blog? I tried to find it but could'nt.

Jonathan Snook said on November 23, 2006

Muneeb: the RSS feed is located in the bottom right hand corner of the page.

Cole Mickens said on November 23, 2006

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 ;))

Muneeb said on November 23, 2006

[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 :)

Dan said on November 28, 2006

Very interesting!

Gorky said on December 27, 2006

Thanks, it is really good!

Ronald said on December 27, 2006

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

Gilson said on March 16, 2007

Onde está o Codigo do DRAG DROP com IFRAME

Till said on March 19, 2007

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?

Everyone said on March 21, 2007

http://www.fatpublisher.com.au/resources_demo.php?id=8

If you move the mouse too fast..
the drag is "dropped" mistakenly.

hacker not cracker said on March 21, 2007

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. :)

Jonathan Snook said on March 21, 2007

@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.

Mike Foster said on April 22, 2007

Another draggable/resizeable iframe demo:
http://cross-browser.com/x/examples/xiframe.html

great article btw :-)

jacobfogg said on May 01, 2007

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?

Blake said on May 05, 2007

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

Paul M. Watson said on June 13, 2007

Nice article Snook. Did you get around to writing the second article on what muneeb requested?

paul said on September 17, 2008

Old, but very fast. Have you any more articles around this?

paul said on September 17, 2008
Jonathan Snook said on September 17, 2008

I should have properly linked this up but this was the follow-up: Determining the Droppable

Philippe Rathé said on October 10, 2008

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.

Cato said on January 16, 2009

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.

Sorry, comments are closed for this post. If you have any further questions or comments, feel free to send them to me directly.