At the time of this writing, there is a plethora of open-source web UI / JavaScript toolkits to choose from. Each one has a slightly different philosophy, but fundamentally, any JavaScript UI toolkit can fit only 2 possible roles:
Low-level DOM manipulation is how it all started, and that remains the key role of JavaScript toolkits to this day, abstracting away the subtle differences in how different browsers implement the DOM API, so that UI developers can focus more on the application logic. But building large UI applications with low-level DOM manipulation alone would require a great deal of time, and mountains of low-level code — it would be as inefficient as driving a car down the freeway in second gear. And so higher-level UI toolkits encapsulating graphical components into widgets came along a short time later to simplify and speed up the assembly of web UI applications. I try to leverage these higher-level widget-based APIs as much as I can.
The Dojo Toolkit started back in 2004, and was one of the first toolkits to become very popular. At first Dojo was strictly a low-level DOM utility, but overtime it grew to become a higher-level UI widget library. Another early JavaScript toolkit was Prototype, which appeared in 2005, but remained a low-level DOM toolkit. The jQuery toolkit, did not arrive until 2006. By that time, the toolkit space was getting crowded with other newcomers such as MooTools, qooxdoo, YUI, ExtJS, and others.
Today in 2011, of all these early toolkits, jQuery became the most popular. It has a certain elegant simplicity to it, and overtime, its user base has contributed a collection of numerous open-source plugins that provides a great incentive to use it. This collection has been cataloged on the jQuery website. In fact, lots of times, the main reason why a web UI developer starts using jQuery, myself included, is not for jQuery itself, but for the nifty 3-rd party plugins that depend on jQuery for deployment.
The most popular 3-rd party plugins end up incorporated into the library and officially supported. And so while the jQuery core remains dedicated to low-level DOM manipulation, there is now an additional jQuery UI library that comes with various stylish UI widgets.
Meanwhile, the once-mighty Dojo Toolkit has stagnated. While Dojo is open-source just like jQuery, the contribution process is not as simple as with jQuery, and the Dojo website does not host 3-rd party plugins like the jQuery website does, so no such collection has developed for Dojo. In addition, jQuery may be more simple to bootstrap and begin to work with for a novice. It can be argued that the Dojo Toolkit has more of a learning curve.
While so far the Dojo Toolkit has not gotten into 3-rd party plugins, which is too bad, to my knowledge, there is currently no other single JavaScript toolkit out there that can match all of Dojo’s key features — and that’s what makes Dojo still relevant and quite useful today, and what this blog is all about.
jQuery alone provides the following key features:
And the Dojo Toolkit provides these additional features, that are especially useful for developing large web UI applications:
The following pair of widgets was originally developed to demonstrate the StorDJE add-on for the Dojo Toolkit. The pair allows the user to store and recall test data, and both widgets consolidate their logic in a common base widget from which they derive their specific functionality. The widget HTML structure is laid out with the Dojo templating system, while the pretty-looking buttons come from the jQuery-UI library. This construction technique easily scales to large applications.
Source code of the common base-widget:
dojo.require('dijit._Templated'); dojo.require('dijit._Widget'); dojo.declare('_EventDataWidget', [dijit._Widget, dijit._Templated], { button_caption: "", button_icon: "", templateString: [ "<table>", "<tr>", "<td valign='top'>", "Event name:", "</td>", "<td valign='top'>", "<input type='text' value='my-event'", " class='input--width'", " dojoAttachPoint='_elEventName'", "></input>", "</td>", "<td rowspan='2' valign='top'>", "<button dojoAttachPoint='_elButton'", " dojoAttachEvent='onclick:_onButtonClicked'>", "${button_caption}", "</button>", "</td>", "</tr>", "<tr>", "<td valign='top'>", "Event data:", "</td>", "<td valign='top'>", "<textarea class='input--width'", " dojoAttachPoint='_elEventData'>", "</textarea>", "</td>", "</tr>", "</table>"].join(""), postCreate: function() { this.inherited(arguments); jQuery(this._elButton).button({ icons: { primary: this.button_icon } }); }, destroy: function() { jQuery(this._elButton).button('destroy'); this.inherited(arguments); }, _onButtonClicked: function() { } });
Source code break-down:
Lines 1 and 2:
Inclusion of dependencies via the Dojo code packaging / modularization system.
The dependencies in this case are special mixins that will be mixed into the base-widget object via the Dojo object-oriented system:
Mixin
‘dijit._Widget
‘
mixes into the object certain attributes and functions so that
it can be used as a self-rendering widget.
Mixin
‘dijit._Templated
‘
mixes on top of the previous mixin, to allow the widget to
leverage the Dojo templating system.
The dependencies are specified in alphabetical order, and internally, each dependency specifies which other dependencies it depends on.
Line 4:
Base-widget object declaration via the Dojo object oriented system. The 2 mixins are listed in the array passed-in as the 2nd argument. Dojo allows an arbitrary number of mixins.
The underscores at the start of object names is a Dojo convention that indicates that an object is a mixin and is not intended to be instantiated directly.
Lines 5 and 6:
These are special settings fields that morph the appearance of the button in the base-widget. The actual settings are overridden by the derived widgets to make the button specific to their functionality.
Lines 8 – 37:
This is the heart of the code, where the base widget template is specified. Hard-coding the widget HTML structure via a plain-text template, rather than programmatically, takes significantly less time, and is also easy to read and examine. And of course, tweaking and modifying the widget HTML structure is significantly easier with a template, and that is the main reason for its usage, while that in turn, is the main reason for using Dojo to construct this widget in the first place.
Some key snippets within the template:
Line 17 — Reference to the text input
‘HTMLElement
‘ is made available within the widget
via an object field ‘_elEventName
‘.
Line 21 — Reference to the button ‘HTMLElement
‘
is made available within the widget via an object field
‘_elButton
‘.
Line 22 — Button mouse click events are made available within
the widget via an event handler method
‘_onButtonClicked
‘.
Line 23 — The contents of widget field
‘button_caption
‘ earlier initialized on line 5 will
be pasted at this point during the template rendering.
Line 33 — Reference to the text area ‘HTMLElement
‘
is made available within the widget via an object field
‘_elEventData
‘.
The ability to associate HTML elements with object attributes, and DOM events with event handler methods, is a highly useful and distinguishing characteristic of the Dojo templating system.
Lines 39 and 40:
Start of event handler that gets called from the
‘dijit._Widget
‘ mixin after this widget, along with
all the derived widgets inheriting from it, gets created. This
handler is analogous to a class constructor, and it is a part of the
Dojo widget lifecycle system. Line 40 is analogous to calling the
super-constructor to initialize any dependencies that the child
object may have.
Lines 42 – 46:
This is the key area where the jQuery-UI library is used with Dojo to render a pretty-looking button. The button is initialized here.
While Dojo has its own button widget, it is nowhere as pretty as the one in jQuery-UI.
On line 44, the configuration field ‘button_icon
‘
initialized earlier on line 6, and by the time this code is
executed, already overridden by derived widgets, is passed to the
jQuery-UI function that constructs the pretty sharp-looking button.
Lines 49 – 53
Event handler that gets called from the ‘dijit._Widget
‘
mixin as this widget is being destroyed, analogous to a class
destructor, and part of the Dojo widget lifecycle system.
The pretty-looking jQuery-UI button has to be destroyed explicitely on line 50.
Line 52 is analogous to calling the super-destructor after all the appropriate destruction has been done in this widget.
Lines 55 and 56:
Event handler function for when the widget button gets clicked on by the user. The name of this event handler has been specified earlier in the template on line 22.
The event handler function itself is empty in the base widget, as it is intended to be overridden by derived widgets, which will perform actions based on their specific functionalities.
Source code of the widget to store test data:
dojo.declare('StoreEventDataWidget', _EventDataWidget, { button_caption: "Store", button_icon: 'ui-icon-arrowthick-1-se', postCreate: function() { this.inherited(arguments); dojo.attr( this._elEventData, 'value', "some text data to store for this event..."); }, _onButtonClicked: function() { this.inherited(arguments); var strEventName = dojo.attr(this._elEventName, 'value'); var strEventData = dojo.attr(this._elEventData, 'value'); dojo.require('stordje'); stordje.publish(strEventName, [strEventData]); } });
Source code break-down:
Line 1:
Child widget declaration, this time deriving from a single parent base-widget.
Lines 2 and 3:
Overriding of parent base-widget’s settings with the settings specific to the functionality of this child widget, in this case the button caption and icon.
Lines 5 – 12:
Leveraging the widget initialization event handler method to
initialize the text area ‘HTMLElement
‘ assigned to the
object field ‘_elEventData
‘ on line 33 of the
‘_EventDataWidget
‘s template to some sample text input.
Line 6 calls the parent widget’s ‘postCreate()
‘ event
handler.
Lines 14 – 22:
Overriding the ‘_onButtonClicked
‘ event handler
assigned to trap button clicks on line 22 of the
‘_EventDataWidget
‘s template with logic specific to the
functionality performed by this widget.
Line 20 lazy-loads a dependency via the Dojo packaging / modularization system.
Source code of the widget to recall test data:
dojo.declare('RecallEventDataWidget', _EventDataWidget, { button_caption: "Recall", button_icon: 'ui-icon-arrowthick-1-ne', _onButtonClicked: function() { this.inherited(arguments); var strEventName = dojo.attr(this._elEventName, 'value'); dojo.require('stordje'); var strEventData = stordje.recallFirst(strEventName); dojo.attr(this._elEventData, 'value', strEventData || ""); } });
Source code of the widget ‘RecallEventDataWidget
‘ is
analagous to its sibling widget ‘StoreEventDataWidget
‘.
In this blog, I demonstrated how to rapidly build up UI application structure leveraging the Dojo Toolkit and its unique templating system and various distinguishing features, while still taking advantage of the sharp appearance of the jQuery-UI widgets.
In addition, the Dojo Toolkit offers a build system for combining and minifying JavaScript for quick loading by the user. Large JavaScript applications should not be deployed without running them through a build system optimization process. Without optimization, a large application will not load within a reasonable amount of time via a typical Internet connection. Like death by a thousand cuts, the loading would get bogged down by the inefficiencies of too many separate HTTP requests. Due to the round-trip nature of an HTTP request, this effect would be particularly noticeable to the more geographically distant web users. Configuring the Dojo build system is outside the scope of this particular blog, but for small to medium applications, I recommend just using this pre-configured custom Dojo build with the templating system dependencies already baked in.
Have fun with Dojo and jQuery!
Copyright (c) 2010-2018 Marat Nepomnyashy
Except where otherwise noted, this webpage is licensed under a Creative Commons Attribution 3.0 Unported License.
Background wallpaper by Patrick Hoesly, used under Creative Commons Attribution 2.0 Generic License.