Yo dojo!

I spent some time this weekend learning and working with dojo.  I have been casting about for some time, hoping to find a suitable replacement for extjs in dxr.  In terms of the lib on a technical level, I liked working with extjs pretty well (I wrote about my first experiences here and here).  What I didn't like was that mid-way through my development cycle, they changed the licensing, making it hard for me to contribute my work back to Mozilla.

I've spent some time evaluating jQuery (which I also like) and YUI, but in the end decided to give dojo a shot: I really want to try and get the accessibility right this time, and David suggested that dojo was up to the task.  I've been following the work of David and others via Mozilla's support of accessibility in Dojo and wanted to see for myself.  Also, I was drawn in by the TooltipDialog widget, which seemed to provide me with a cool way to mash a ton of static analysis data into an MXR-style source file (think Google Maps location bubbles, but for info in source code).  With this rewrite of dxr I'm moving away from the "desktop app" feel and more toward a document-centric approach, so the advantage extjs had before is somewhat lessened.

First reaction to dojo?  It feels nice.  I really hate having to learn something new, because inevitably documentation is not aimed at me: I'm not a beginner, and I'm usually looking for a very quick intro so I can get started, vs. a long drawn out tutorial (see Atul's Python for JS devs as an example of what I like).  I was pleasantly surprised with the Dojo Quickstart Guide.  It was the sort of thing I could skim and get the basic lay of the land.  Even better, it had lots of links to more complete sections of the Book of Dojo.  Jumping back and forth between these two was enough for me to feel like I understood what was possible.

Next I wanted to see the API reference.  The extjs API reference is usually pretty good.  I found the dojo API reference to be about the same, in terms of ease of use in finding what I was after.  One thing I don't love about the dojo API reference is the lack of consistent examples: sometimes you'll get a half-dozen good uses of a function, and other times the example is the same as the signature (e.g., telling me it takes a String, when I can see in the code it wants specific string values is not that helpful).  In any case you get what you pay for, and I'm paying only with my time.

As with extjs, it only took me a few hours to hit the limits of what it was meant to do, compared to what I wanted to do.  In this case, I really wanted to dynamically bind TooltipDialogs to anchor links: for every token in a source file for which I have static analysis, I want to allow the user to click it and pull that info via xhr into a pop-up bubble.

It turns out TooltipDialog is meant to be used with a DropDownButton.  Fail.  So my options are:

  • Use DropDownButtons + TooltipDialogs and style them as links, which will explode the overall size of the html I send users (it's already too big)
  • Fake a DropDownButton + TooltipDialog (e.g., style="display: none") and then do DOM tricks to position things correctly (cf. this suggestion)
  • Break the connection between DropDownButton and TooltipDialog and manage state, position, focus myself
    I tried all 3 ways, and everything has its own set of problems.  Eventually, with a suggestion from David, I got it working like this:

  • Create a TooltipDialog in the body, styled to "display: none" with an id="ttd"

  • Add the following code:
  dojo.require("dijit.TooltipDialog");  

  dojo.addOnLoad(function() {  
    var ttd = dijit.byId("ttd");  

    // XXX: deal with user clicking outside popup  
    dojo.byId("ttd").addEventListener("blur",  
    function(e) {  
      dijit.popup.close(ttd)  
    },  
    true);  

    // only work with anchors that aren't for line numbers (.ln)  
    dojo.query("a:not(.ln)").connect("onclick",  
    function(e) {  
      e.preventDefault();  
      dijit.popup.open({  
        parent: this,  
        popup: ttd,  
        around: this,  
        onCancel: function() {  
          dijit.popup.close(ttd);  
        }  
      });  
    });  
  });

Here I'm grabbing all the source code token links and rewiring the onclick event so that it pops-up the TooltipDialog near the link (around: this).  Also, I'm having to use vanilla JS in order to get the onBlur event to work properly: I want to make it so that when you click outside the pop-up it vanishes.  Nothing I can do via pure dojo (e.g., dojo.connect(ttd, "onblur", this, function...)) will get me that event. UPDATE: this does in fact work, and work wonderfully:

dojo.connect(ttd, "onBlur", function() { dijit.popup.close(ttd); });

To be honest, this all feels like a horrible hack (it's possible it is, and some kind reader will set me straight!).  Anyway, it's working now, and I hope to have a functioning prototype based on this in the coming weeks.

To close I'll just add that I'm enjoying the #dojo channel on freenode, too: dojo seems to have a thriving community around it.  That's refreshing vs. extjs, where I always heard my voice echo off the back of their irc channel and commuinity forums.  So far so good with dojo, and more to come soon to a dxr near you!