Donut DragDrop Release

Donut DragDrop is a fast, lightweight JavaScript dragdrop library for JavaScript programmers. It is not completely plug and play but almost. It doesn't try to be everything to anyone with fancy things like constrained motion as part of the base implemenation. You can add layers on top to add more functionality.

Genesis

First, I had the Donut DragDrop idea. This allows for native browser events to detect the interaction between one or more dragged items and a drop target. I tried implementing it and it worked quite well. My JavaScript for the dragdrop manager was nice and object-oriented but bulky and each draggable and target had an associated JavaScript object.

During a long discussion on the Yahoo! JavaScript discussion group , Tim Hosking suggested ideas for a very lightweight dragdrop manager that might work well for many dragdrop implementations. Instead of a JavaScript object for each draggable and target, Tim suggested using CSS class names "draggable" and "target" to identify these. In this implementation I have used Tim's ideas and blended them with my donut proxy idea.

On comp.lang.javascript in another long discussion Richard Cornford outlined a way of building layered JavaScript libraries to facilitate small downloads with only the necessary JavaScript for the given implementation but still allowing flexibility and extensibility in the library. I have used my interpretation of Richard's ideas in this dragdrop implementation.

I have used the Yahoo! UI yahoo.js, dom.js and event.js files. I have taken a few ideas from the Yahoo! UI dragdrop.js file but not the general attitude of that or other dragdrop libraries. All the dragdrop libraries I've tried attempt to be everything to everyone and grow larger and less flexible. For example, the minified Yahoo! dragdrop.js is 24 kB but my DragManager.js file minified is only 4kB. The Yahoo! library doesn't allow for easy trimming of the code to only that which is needed. Also the Yahoo! library is less flexible and more difficult to extend than my Donut library.

Examples

The Square Donut Proxy example is the simplest implemenation. Just drag and drop.

In the Jelly Donut Proxy example you can use the alt key to select more than one item for dragging.

Source

The code for this dragdrop library is available by SVN with the command

svn co http://dev.michaux.ca/svn/dragdrop 

You can browse the source at http://dev.michaux.ca/svn/dragdrop The source is available for your use under the MIT license .

The code is partially documented using JSDoc but JSDoc has deficiencies and cannot pickup all the documenation in the source. You may need to look in the source to see certain documentation.

Update February 10, 2007: This code as been incorporated in Fork

Comments

Have something to write? Comment on this article.

Carl Johansen October 2, 2006

Brilliant! I love it! I have one problem. Sometimes on IE6 I get into an infinite loop between handleMouseUp and handleMouseMove in DragManager. handleMouseUp() has an "Internet Explorer hack to see if button was released outside of window", which calls handleMouseMove(), which calls handleMouseUp(), which calls...

I worked around it by adding a "skipCheck" boolean parameter to handleMouseMove(), and passing true when making the call to handleMouseMove() in handleMouseUp(). If skipCheck is true, I don't do the IE hack check.

I also have a suggestion. I added a few lines in DragManager.js:

isDraggable: function(element) {
  return (YAHOO.util.Dom.hasClass(element, "draggable")) ? true : false;
},

////////////// START NEW ////////////////  
isNonHandleArea: function(element) {
  return (YAHOO.util.Dom.hasClass(element, "nonHandleArea")) ? true : false;
},
////////////// END NEW ////////////////

// returns a draggable HTMLElement
onDraggable: function(e) {
      var targ = YAHOO.util.Event.getTarget(e);
  while (targ) 
  {
    if (this.isInvalidHandle(targ))
      return null;

    ////////////// START NEW ////////////////      
    if (this.isNonHandleArea(targ)) 
      return null;
    ////////////// END NEW ////////////////

      if (this.isDraggable(targ)) 
      return targ;            

    targ = targ.parentNode;
  }
      return null;
},

Now, within a "draggable" container element, I can declare "nonHandleArea" elements (ie class="nonHandleArea"). These elements will not be draggable, but they will drag if a draggable part of the overall container is dragged. This could be useful, for example, in making a "window" that can be dragged only by its "title bar". Just declare the window border "draggable" and the inside content area "nonHandleArea".

Peter Michaux October 2, 2006

Carl,

I'm glad you like it. Thanks for the comment.

I was uncomfortable with the IE hack for the simple reason that it is a hack and the way it was done was very brittle. Also it was the one thing I hadn't tested much and just copied form Yahoo! UI. Bad me. You'll see that in the svn trunk I have commented out this hack.

DragManager.js

Your skip check idea is a good one if you want to keep the hack. I'd have to take a longer look but since this hack is gone for me I'm working on other things. IE is only 60% of the market now, I believe :)

The concept of layers is really important to the minimal design of this dragdrop library. I want DragManager.js to do as little as possible. You'll see in svn it actually does less now than before.

I don't want DragManager.js to know about handles. (There is an exception for links since clicking a link should follow the link instead of start a drag operation.)

So in the spirit of layering the JavaScript I recommend something like the following to add the non-handle area concept on top of DragManager.js. After including the DragManager.js file add this somewhere.

DragManager.prototype.isInvalidHandle = function(element) {
  return ((element.tagName && element.tagName.toLowerCase() === 'a') ||
          YAHOO.util.Dom.hasClass(element, "nonHandleArea")) ? true : false;
}

Now you get what you want, the DragManager.js tested code doesn't need to be touched and potentially break, and the file size doesn't bloat as more people request more features.

This also gives incredible flexibility as you could override isInvalidHandle() so that children of a div with id="asdf" are not handles.

You could get fancier and subclass DragManager and then use a call to superproto() like I have done in a few examples.

svn trunk is in a state of flux but check out the changelog to see what I've changed already and the direction I am going in getting ready for v0.2. There aren't even the interesting moment hooks anymore which put a huge smile on my face. I can just imagine someone asking for a hook in another place in the code. No bloat thanks when OOP makes these things possible without hooks.

Changelog

Have something to write? Comment on this article.