Quick Fixes and Dojo Support

By on October 21, 2008 7:55 am

A lot of the stock Dijit components are single-serve, meaning they only solve one style of problem. But Dojo is very flexible, and can work most any way you imagine or intend — you just have to put on your coder hat, and bend away. That’s where I come in. One of the things I do most often in my role as lead support for SitePen’s Dojo Support offering is to find simple solutions and workarounds for problems encountered when the widget code doesn’t behave exactly as you want, or otherwise needs some level of professional bending.

Something that has come up time and time again is a balance between user interface paradigms provided by widgets found in Dijit. Take for instance an AccordionContainer: it displays a number of title headings within a confined space, and uses the remaining space for the content area of each pane. The size of the content area is calculated based on the available space, and adding many additional AccordionPanes to the container eventually leaves you with very little space. Adding panes indefinitely will leave you with zero space for content, which is a bad user interface no matter which way you look at it.

The pattern here is: a fixed area, a fixed content area (calculated), and only one available visible pane at a time. The TitlePane widget on the other hand does the opposite: it sizes its content area to the size of the content, wiping in and out. TitlePane is standalone, and doesn’t communicate with other TitlePanes. It has two states: open and closed (okay, three: in transition). The benefit here is not needing to know the size of your content before you add it, but you lose the paradigm of having only one open pane at a given time in an automated fashion. With a series of TitlePane’s stacked upon one another you can have an infinitely long list of children with variable heights and never worry about cramped space or bad UI, though all the panes could potentially be in a closed state, or all open. What if we wanted it to be more Accordion-y?

Like most things in life, somewhere between two extremes rests a balance. That’s my job — finding these solutions. During a screen-sharing exercise with a client, I quickly put together a solution to the aforementioned user interface complication, and have been reusing the technique enough to constitute explaining it out loud. It comes up occasionally as a question in the Dojo forums and on IRC, and when I spot it, I provide this little paste of code:

// define the TitleGroup widget:
dojo.declare("dijit.TitleGroup", dijit._Widget, {

	postCreate: function(){
		this.inherited(arguments);
		this.connect(this.domNode, "onclick", "closeAllBut");
		
		// slightly foo: works in this use case, but this will close 
		// all TitlePane's on a page, even the ones not in this group! 
		// FIXME: add code to scope down could make 
		// addChild/removeChild function to handle connections?
		this.connect(dijit.TitlePane.prototype, "_onTitleKey", "closeAllBut")
	},
	
	closeAllBut: function(e){
		// this closes all found titlePanes within this domNode excluding
		// the one the click event originated from.
		var d = dijit.getEnclosingWidget(e.target);
		if(d){
			dojo.query(".dijitTitlePane", this.domNode)
				.forEach(function(n){
					var tp = dijit.getEnclosingWidget(n);
					if(tp && tp.open && tp !== d){ tp.toggle(); }
				})
			;
		}
	}
});

You can see the overall result versus the Accordions, where we’ve created a custom wrapper widget that manages the TitlePanes contained within, producing the Accordion-like behavior of having only one open pane at any given time. After ShrinkSafe, it is 446 bytes of enhancement — custom tailored for a specific use case, albeit a common one. With roughly ten lines of code, we’ve bridged the gap between the Accordion and TitlePane UI patterns, and created a really simple widget to compartmentalize this “hack” we’ve put in place.

This, and any other level of extension or reuse, is available because of the modular and functional approach Dojo and Dijit take to problem-solving, and why I enjoy getting my proverbial hands dirty in finding these kinds of workable solutions. Developer overhead for inventing the TitleGroup: thirty minutes. We just needed to look at the TitlePane code, determine which function was the delegator (decider?) of the open/close state (_onTitleKey handles the enterkey when the title has focus, and delegates onclick handling withing the TitlePane, so it was the safest route: we’ve retained accessibility!), then reusing publicly exposed API for managing the children.

There is a note above in the postCreate method where we’d created connection to the TitlePane.prototype, meaning any instance of a TitlePane will fire this function. The solution to the caveat listed in the comments would be to implement addChild and removeChild functions, tracking the individual children when they come and code, and connecting to the instances passed rather than generically across all instances. Fortunately, that was not a requirement for this use-case, but it may be for you.