Drag n Drop with Dojo

This article could more be more aptly named “@#$@#$@)* with Dojo.” This is a classic example, IMHO, of where Dojo documentation falls flat on its face. The doc page for drag and drop (aka dojo.dnd) is more of a method and property summary of all the objects that work together to make drag and drop work in Dojo. It doesn’t do a very good job of explaining just how all those objects work together, at least not in an easy to digest manner. There’s also a lot of information there that, frankly, just doesn’t matter to the developer just trying to get some simple drag and drop widgets on his or her web page.

Also missing are practical examples that exist elsewhere in the documentation. In fact, that is one thing that Dojo’s documentation does do well: lots of examples. For someone like me—who has an easier time learning by looking at code examples than wading through textual explanations—that’s a god send. There is one fairly detailed example of a shopping cart I found at sitepen.com. However, it has one major shortcoming: it completely ignores the backend that would drive any real world shopping cart. So, here’s several hours of googling and swearing boiled down to “The quick and dirty guide to using drag and drop with Dojo.”

Getting items on your web page that drag and containers to drop them on is actually quite easy, almost to the point of being ridiculous. Since I think validated HTML is over hyped (a topic for another day), I prefer to use Dojo’s HTML attributes when creating widgets. So how hard is it to get something on the page that drags and drops?

<div dojoType="dojo.dnd.Source" id=source1>
   <div class="dojoDndItem" dndData="A">Item 1</div>
   <div class="dojoDndItem" dndData="B">Item 2</div>
</div>
<div dojoType="dojo.dnd.Source" id=source2>
   <div class="dojoDndItem" dndData="C">Item 3</div>
   <div class="dojoDndItem" dndData="D">Item 4</div>
   <div class="dojoDndItem" dndData="E">Item 5</div>
   <div class="dojoDndItem" dndData="F">Item 6</div>
</div>

That’s all there is to it. You can drag and drop items between your two sources to your heart’s delight. But what happens when you’re ready to record the changes that have been made? What if you’d rather use ids over text strings to record information back to the database? That was where the @#$@#$)&!! came in.

Let’s start with using dojo.connect to catch events. Trying to get a handle on a node with dojo.byId(“source1″) isn’t going to work, presumably because it’s only going to return a DOM node and not a Source object. Drag and drop sources are not dijits, so dijit.byId(“source1″) isn’t going to work either. If that’s the case, then how do you get a handle on your source? Well, O’Reilly to the rescue. In the chapter on drag and drop, Dojo: The Definative Guide has a line like this in one of the examples.

source1 = new dojo.dnd.Source("source1");

Seems a bit redundant to me, but it works. Now you can use ’source1′ as the object to connect to and it doesn’t seem to cause any problems that you’ve already defined source1 as a source with dojoType=”dojo.dnd.Source”.

Next is the onDrop event. Dojo’s documentation seems to indicate it’s the “preferred” event to catch, except that it doesn’t work. Nothing I could connect to seemed to be firing onDrop. O’Reilly and other sources use onDndDrop which does work, so I ended up with a connect statement that looks something like this:

dojo.connect( source1, 
   "onDndDrop", 
   function(source, nodes, copy, target){
      //your code to work your magic goes here
   }
 );

The parameters are fairly self explanatory. “source” is the source object where the draggable nodes originated. “nodes” is a collection of the items being dragged. “copy” is a boolean indicating whether or not the nodes should be copied to the target or moved from the source. “target” is the source object on which the nodes were dropped.

However, in practice I’ve ended up using the event onDropExternal much more often. Basically this means the item that was dropped on a particular target started out in a different source. Most of the time this probably what you want to catch and it saves you the trouble of adding logic to compare the source and target parameters and respond accordingly.

Whichever event you use, you can iterate over the nodes array passed in and take whatever action is necessary. However, the node array is a collection of DOM elements. There’s no Dojo goodness attached to them. In my earlier code example drag and drop items included a dndData=”n” attribute. This data will not be available in the node array. Each source keeps a hash with information about the nodes it contains. The hash is indexed on the id attribute of the node (which dojo will create if you don’t specify one.) That is where the value you put in dndData ends up. So in order to get at the data you need to do this:

for( i=0; i < nodes.length; i++){
   item = this.getItem(nodes[i].id);
   my_data = item.data;
}

Remember, this code is in and event handler so “this” is the source object that fired the event. Do you have to use dndData? No. If you don’t specify dndData, item.data contains the innerHTML of the node. So taking an item from my first example:

<div class="dojoDndItem">Item 1</div>

item.data would contain “Item 1.” Can you specify complex data via HTML and dndData, say with JSON notation? No. You could probably use your own HTML attributes. Something like

<div class=dojoDnDItem myid="123" myname="ABC">DnD Item</div>

would work (theoretically), and then you could then use dojo.attr(node,”myid”) to retrieve the data. That sounds like it should work, but I haven’t tried it. I stuck with using the hash for a couple of reasons. First, if there is a facility already provided, I don’t see any reason to hack something else. Second, adding items to your source containers via Javascript will populate the hash so you may as well get in the habit of using it.

So are you home free? Not just yet. There’s one more gotcha that could trip you up. Remember in my first code example I used dojoType=dojo.dnd.Source? There is also dojo.dnd.AutoSource. What’s the difference? Where you look for your data. If you use dojo.dnd.AutoSource, this.getItem(“node id”) will work. If you use dojo.dnd.Source, the data will still be in the source object and not moved to the target source object when the drop event fires. That means you’ll have to change “this” to “source” to get at your data. I’m sure someone came up with some example implementation where the distinction makes sense. I haven’t found a need for it yet, so I have always used dojo.dnd.AutoSource.

I hope you found this helpful. In my next post I’ll talk about populating DnD sources on the fly. If I’m feeling really ambitious, I might talk about things to watch out for when subclassing dojo.dnd.Source as well.

Wrestling with Dojo

I’ve recently started working with Dojo, a javascript framework that has a lot to offer if you’re planning on building a large, complex web app. The only other framework I’ve used is jQuery, which I like a lot. However, jQuery doesn’t lend itself well to managing lots of javascript code. I’m not sure how much of that is a function of jQuery and how much is a function of my coding practices, but there is at least one other person out there who agrees with me. In fact, I stumbled onto this post of Addy’s after having an “ugh, there’s got to be a better way to do this” moment shortly after starting a large project using jQuery. That post led me to take a look at Dojo.

So after a couple of weeks wrestling with Dojo, here are some pros and cons.

Pros

  • Dojo does it all. OK. Maybe not, but Dojo handles natively what you would end up needing several plug-ins or custom code to accomplish in jQuery.
  • Dojo only loads the code you need. You have complete control over which libraries are loaded and when.
  • Built in parser. This is perhaps one of the nicest features of Dojo. One of the biggest headaches with jQuery is making sure code loaded via Ajax is initialized properly. live() will only get you so far. Takes care of that for you.
  • Use HTML to define everything if you want. Don’t want to write a bunch of JavaScript code to instantiate everything. You don’t have to. Dojo’s parser looks for parameters in the HTML tags and does it for you. This would end up in the cons list if you’re a stickler for validated HTML code. If you are, you can put it all in JavaScript.
  • Excellent form widgets. Yes, jQueryUI has form widgets but Dojo has a more complete set and I prefer the functionality of Dojo over jQueryUI. My “Yes!” moment was when I discovered Dojo’s calendar input doesn’t disable the textfield like all the jQuery plug-ins I have tried. You want to just type in your birthdate instead of clicking *mumble mumble* times to finally get back to your year? Go right ahead.

Cons

  • Learning curve. One thing jQuery has going for it is it is dead simple. I know I looked at Dojo back when I was first looking at JavaScript frameworks to spice up user interaction. I’m pretty sure I went with jQuery because it was so easy to pick up and get started with. Dojo not so much, particularly if you get into the Dijit (Dojo widet) libraries.
  • Documentation. Like most open source projects documentation leaves much to be desired. Add to that that many tutorials and hints you can find on the web refer to older versions of Dojo and usually don’t apply any more. Even Dojo’s documentation has outdated information or links to pages that are missing. The Dojo folks know this is a huge shortcoming and are working on it, but in the meantime, this only exacerbates the learning curve.

So far I really like what Dojo can do, but the documentation issue has been a big one. I’ve wasted hours looking for answers to my questions only to find them when searching for something unrelated. So, I’ll try and document my adventures with Dojo here to hopefully save others from the headaches I’ve dealt with. As of this writing Dojo is on version 1.6.

Sliding Doors

So I figured it was time actually do some work on my site. I haven’t touched it since 2005. Honestly, I haven’t really needed to. I’ve been keeping busy enough that marketing myself wasn’t really necessary. But then I’ve never tried to survive a depression either.

One of the things I wanted to do was use jQuery on the site, mostly to demonstrate that I could. I stumbled onto some web pages talking about jFlow, probably in one of the blogs I follow. Other examples of this effect are the Coda website (a code editor for Mac) and the Giant Creative web site.

So I downloaded jFlow and tried it. First impression: buggy. It doesn’t always animate . . . forward anyway. With my initial implementation, slide 1 to 2 jumps. No nice scrolling. I see that behavior between the second to last and last slide as well. Things work peachy going backwards from 2 to 1, though. Go figure. I didn’t really want to spend time digging into the code to see if I had done something wrong or to fix it so I went looking for another solution.

Enter Coda Slider 2.0. First impression: meh. Looks like it has lots of features, but the documentation leaves much to be desired. It’s not clear in the example HTML which CSS is required for the plugin to work and which is just layout. Perhaps the most off-putting aspect of Coda Slider is it seems to dictate (and thus limit) your layout. Again, with more time I could probably have figured out a way around these alleged limitations, but that isn’t how I wanted to spend my time. So, I kept looking.

After a very short search, I found this tutorial over at jQuery for Designers. While it doesn’t have the advantage of a single plugin, in fact it takes three, it provides all the functionality of either jFlow or Coda Slider and then some. As an added bonus, it is extremely flexible: put your navigation anywhere you want, anyway you like and it works. The final cherry on top? It worked the first try. Check it out.

Of course, now that I have it fully implemented, I’m questioning the choice, but that’s okay. That’s what we call agile development. If you don’t like the way it’s working, change direction.