New dragdrop recipe: donut dragdrop
A look at two current dragdrop recipies' pros and cons. A new recipe (the donut recipe) that might be better, faster and simpler in many situations.
Hidden layer recipe
The second recipe I know about avoids looping on every mousemove event. I'm told this is in the book DHTML Utopia but I did discover this by accident one day. In this recipe, when the page is loaded or a new target is added, a new hidden element is created at a very high CSS z-index for each target element. The hidden element has the same location and dimensions as the visible target element. Browser event listeners are attached to this hidden element. The dragged element is dragged at a lower z-index than the hidden elements (ie. between the hidden element and the visible target elements.) When the cursor and dragged element enter a target, the cursor is directly over the hidden element so the listeners fire. This is great and avoids looping with each mousemove but there are problems. It takes time to check size and location of each hidden element at the beginning of each drag. This time could be substantial for targets deep in the DOM tree. A problem still exists for nested and overlapping targets. It is complicated to create and manage the hidden elements in the same stack order as the corresponding visual target elements appear to the user.
My donut recipe
The main problem with the looping recipe is the dragged element is in the way and browser events cannot trigger when the cursor enters a target region. The hidden layer recipe avoids this problem but requires the creation of a complicated hidden layer when the drag starts. I don't want to loop. I do want to use browser events but I don't want to create the hidden layer. I haven't seen my solution elsewhere so this could be new.
For my donut recipe, when a drag is initiated a drag proxy is created from four divs. This looks like dragging an empty, square div with a border centered under the cursor. However each of the four divs is used to create one side of the square. This composite proxy is like a donut centered under the cursor. However none of the composite proxy donut is actually under the cursor. This means I can attach listeners to the actual target elements at the beginning of the drag. No looping with each mousemove and no hidden layer! Also the targets are naturally stacked as they appear to the user so nested and overlapping targets are no problem.
creative donut appearance
The donut recipe uses a composite proxy because a hole in the proxy must be under the cursor. This doesn't mean the proxy must appear to be a boring square border. A boring square will be fast but there are other options. The donut proxy "border width" can be made so big that the donut hole is only a pixel or four or nine etc. The four divs can be filled creatively with images or clones of what is being dragged. Some clever CSS using the overflow property could make the donut look almost exactly like whatever the user wishes to drag.
You could clone the original elements to the donut and hide the originals. Then the user wouldn't even know they are dragging a proxy except for the small hole inside that is only visible sometimes. Often the cursor will actually cover this hole.
The hole could be closer to one edge or corner of the proxy by having the four divs with different sizes and shapes. As long as the hole is under the cursor the donut idea will work.
Also you don't have to use all four divs to make the composite proxy. You could use zero, one or ten. As long as nothing is directly under the cursor during the drag so the browser can detect the events as the cursor moves over and out of the targets.
This is development code for a dragdrop library that will be capable of all the things I need (multiselect, strange shape draggables and targets, etc.) This shows the nested and overlapping targets working for Safari 1.3 and Firefox 1.5 on Mac and IE 6 and Firefox 1.5 on Windows XP.
The code is currently copyrighted.
The donut recipe solves many problems with current dragdrop recipies. The tradeoff is some minimal inconvenience in creating proxy with acceptable appearance. Situations without targets don't need the donut method. Some situations may not be slow with the looping recipe and may not have nested or overlapping targets. Some or many situations with many targets may benefit greatly from the donut recipe.
Update (Aug 10, 2006)
Please see my Donut DragDrop Release article for a release version you can use.
Have something to write? Comment on this article.