This article is a follow-up on my previous blog regarding a dijit tree with checkboxes. In the past couple of weeks I received a number of requests if it would be possible to get the checkbox tree with so called multi or triple state checkboxes. By default a checkbox has two states:

True (checked)

False (unchecked)

This BTW: applies to the default HTML checkbox as well as the dijit checkbox so in order to make a multi/triple state checkbox work we have to create our own checkbox that holds an additional state. Second, we have to be able to visually represent the third state. Going forward I will refer to this third state as the ‘mixed’ state.

Click here to take a quick look at a demo. Please let me know what you think, leave a comment or ask a question. The source package for the Checkbox Tree with Multi State Checkboxes can be downloaded here.

The default dijit checkbox uses the standard HTML checkbox controls but overlays the visual image with its own sprite and apply some CSS styling. We are going to use the same concept by extending the dijit checkbox widget, create a new sprite and associated CSS classes. To make it real fancy I’m going to create separate sprites for each dijit theme. Having said that I still want to be able to support the default HTML checkbox, the visual aspect of a mixed state will not be supported BUT at the application level you will be able to check if the HTML checkbox is in a mixed state.

For this tutorial I’m going to use the dojo version 1.4 source of our CheckBoxTree which can be downloaded here, just in case you are wondering: will there be a dojo 1.3.2 version available of the multi state checkbox tree, the answer is yes.

The Multi State Checkbox

I’m going to include the extended dijit checkbox widget in our CheckBoxTree source file as I currently don’t have any plans for using the multi state checkbox anywhere else. However, it is very easy to extract it if you want to. And now for some code:

On line 1 we tell dojo that our CheckBoxTree file also provides our new checkbox using the same namespace (tmpdir) as we did before. We include the dijit checkbox on line 6 and declare the new checkbox on line 9 which inherits from dijit.from.Checkbox.

On line 11 we define the base class for our checkbox widget. This will distinguish our checkbox from the default dijit checkbox allowing us to create our own CSS classes and thus introduce the third (mixed) state. On line 13 we set the value attribute to “unchecked”. We are going to use the checkbox value attribute to hold the mixed state which can be either “checked”, “unchecked” or “mixed”. The value attribute maps the standard value property of the default HTML checkbox and therefore at the application layer you can use this value to determine if a checkbox is in a mixed state regardless if you use our dijit style checkbox or the default HTML checkbox. (more on this later).

On line 15 we overwrite the _setStateClass method which will generate the appropriate CSS classes depending on the current state of the checkbox. For example, if the checkbox is checked it will generate a CSS class “tmpdirCheckBoxMixed” if on the other hand the checkbox is in a mixed state it will generate a CSS class “tmpdirCheckBoxMixed” or we could get a state like “tmpdirCheckBoxMixedHover”.

Next we are going to overwrite the default set and get ValueAttr functions in order to be able to use the checkbox value attribute to hold our mixed state.

The default _setValueAttr method also manipulates the ‘checked’ attribute of the checkbox and that is something we don’t want. Believe it or not but that’s all we have to do for our extended checkbox, next we will work on our CSS class definitions and create new sprites for our checkbox.

CSS Classes and Sprites

First we have to create a couple of new directories to support our creative work. In your tmpdir directory create a subdirectory called ‘themes’ and the themes directory needs another subdirectory for each dijit theme. Finally each dedicated themes directory will have a subdirectory called ‘images’. Your directory structure should look like this:

This directory structure follows the dojo/dijit directory structure, if you want to change it feel free to do so but for this tutorial we are going to use the above structure. In the \themes\nihilo\ directory create a new file called tmpdir.css and add the following lines:

The only thing missing from a creative standpoint is the sprite itself which we declare as our background image on line 17. A sprite is basically a single image composed of multiple smaller images, loading a single sprite instead of multiple smaller images improves performance and creates less overhead and eliminates screen flickering. There are many, many image editors on the market you could use to create the sprite but I prefer to use IconWorkshop from Axialis because the professional version has special support for sprites or Image strips as they call them. Lets create the spriteCheckbox.gif file in the \themes\nihilo\images directory.

As you can tell from the above image the sprite is composed of 9 smaller images, each 16 pixels high and wide representing the different states of our checkbox. The three images on the right are the hover states which currently are the same as the enabled states on the left. Feel free to adopt the image to your own needs, both the PNG and GIF version are included in the source package.

You will have to repeat the above steps for the other dijit themes Soria and Tundra but I guess you get the picture and don’t forget to replace ‘.nihilo’ in the CSS file with the appropriate theme you are creating. (i.e: ‘.tundra’ and ‘.soria’)

Update The Checkbox Tree

To incorporate the new checkbox widget in our tree we will have to make enhancements to all three components of our tree, these components are:

The Model

The Tree Nodes

The Tree

Update The Model

The first thing we will have to do is to introduce a new attribute for each store item which will hold the additional mixed state of our store item. As I said in the previous tutorial the data store and model have no direct relationship to any of the TreeNodes or the associated checkboxes (remember the MVC pattern). Also, as of dojo 1.4 there may actually be multiple checkboxes referring to, or should I say representing, a single store item.

On line 6 we introduce a new attribute to our model called ‘checkboxMState’ which holds the name of the attribute we will be using to store the mixed state for each store item. Typically there is not need to overwrite the ‘checkboxMState’ attribute unless you are planning on using the attribute name “mixed” in your Json file. If so, you will have to overwrite the checkboxMState value when creating the model.

First we need to enhance the getCheckboxState method to support the additional mixed state.

Instead of passing two states around we create a object called ‘state’ with two properties: checked and mixed. On line 111 and 112 we copy the store item states to our new object and return it at line 122. Now that we can retrieve the additional state we must also be able to store it.

The most important thing with the updated _setCheckboxState method is that we will now have to validate BOTH states in order to determine if an update to the actual checkbox(es) is required. For example: if a child checkbox is checked/unchecked the parent’s checked state may not change but it could alter its mixed state and thus we will have to let the tree know it needs to update the parent checkbox(es).

The next method of our model we need to address is the _updateParentCheckbox. Here is were we determine if a checkbox gets a mixed state, only parent checkboxes can get a mixed state. If any child has a mixed state the parent will automatically get the mixed state too regardless of the states of the other children.

If a store item gets a mixed state, by default it will also enter the checked state (line 193). We could have gone both ways but if you opt to use the default HTML checkboxes (which do not support the visual aspect of a mixed state) at least you can SEE some child is checked if the parent branch is collapsed. I guess this is like the glass is half full/empty discussion. On lines 182-189 we explicitly check for ‘true’ or ‘false’ as the checked state could also be ‘undefined’ in which case we need to ignore it. Remember, the undefined state occurs when no ‘checkbox’ attribute we specified for the store item in the Json file and the model ‘checkboxAll’ attribute is set to false.

The last changes I made to the model is how the initial store data validation takes place. During some performance testing I found that loading the entire Json file at once, instead of partial or lazy loading, offered by far the best performance. On average loading the file this way is about 5-6 times faster than partial loading. Also, keep in mind that if a strict parent-child checkbox relation is required (which is the default) we will have to load the file first anyway to perform our initial store data validation. As a result I broke the original validateData method into two pieces.

We walk down to the lowest branch of the tree and request a parent update for at least one child on the branch. The updateParentCheckbox method will go back up the tree all the way to the root.

Update The Tree Nodes

In the original version of our Checkbox Tree the Tree Node simply created a HTML checkbox and inserted it into the DOM. In this episode we are going to offer the option of using our enhanced dijit checkbox widget or use the default HTML checkbox. In addition, I wanted to offer the option to hide the Open/Closed folder icon and/or the leaf icon preceding the node label.

In order to accomplish these additional requirements we are going to introduce four more attributes to our Tree:

On line 263, as before, we fetch the checkbox state from the store however, this time we get an object back instead of a simple state. If the checked state is undefined no checkbox is required for this tree node. On line 265 we check the new ‘checkboxStyle’ attribute to determine what type of checkbox is needed. On line 275 we first check if this particular tree node is expandable (is it a parent checkbox?), than if the Open/Closed Folder or leaf icon aren’t required we simply remove the appropriate CSS class from the icon Node.

Previously, when a store item changed state the model would inform the Tree and the Tree would then simply update the checkbox checked state accordingly. This time around we need a little more sophisticated approach because we are now dealing with two states and two checkbox styles each requiring different ways of storing the state information. We add a new method to the CheckBoxTreeNode called _setCheckedState which will handle the updates for the actual checkboxes. This method will be called by the Tree as soon as it receives an update notification from the model.

On line 288 we first determine the checkbox style, next we check if multi (mixed) state support is required. On line 297 you can actually see that even if we use the default HTML checkbox we can still support mixed states. In other words, under the hood we support multi state checkboxes regardless of the checkbox style.

Update The Tree

Last but not least we have to make some minor changes to the Tree as well. I already talked about the additional attributes:

I also talked about the Tree no longer simply updating the checkbox checked state, going forward the _onCheckboxChange method of the Tree will call the _setCheckedState method of the CheckBoxTreeNode to have both states (checked and mixed) updated.

Well, that’s it for our CheckboxTree and extended Checkbox widgets, the only thing left to do is to update our index.html file to include our new CSS file and add some of our new attributes. The new attributes aren’t really necessary if you are Ok with their default value. I have included them just to show how to overwrite their default values if needed.

The only real import change we are making to our index.html file is on line 7, the inclusion of our CSS file. Please make sure the tmpdir theme is the same as the one you select for dijit on line 6.

If the CSS themes do not match your are NOT going to see any checkboxes unless you set the checkboxStyle attribute to anything else but “dijit”.

Below you see three different representations of the same Family Tree using some of our configurable attributes. These examples are all using dojo 1.4 to demonstrate the multi parent support. For example: In our Json file Chantal is referenced by both Peter and Mary as a child. In addition, Peter is defined as a ‘parent’ and but also referenced by John as a child.

The example on the right uses the default HTML checkboxes BUT, as mentioned before, under the hood it still maintains the mixed state and thus your application can check if any of the parent checkboxes (John, Mary, Peter, Joan and Chantal) are in a mixed state. You could disable the mixed state support by setting the checkboxMultiState to false.

Checkbox Tree Configurable Attributes

Over the course of these two tutorials we have introduced a number of attributes to our CheckBoxTree and CheckBoxStoreModel widgets. The tables below list all attributes, default value and a short description.

Note: Any attribute that has a default value assigned is considered optional.

CheckBoxStoreModel Attributes

Attribute

Type

Default

Description

checkboxAll

Boolean

true

If true, every node in the tree will receive a checkbox regardless if the attribute ‘checkbox’ is specified or not for a dojo.data item. If the ‘checkbox’ attribute is not specified the default value checkboxState will be applied.

checkboxIdent

String

“checkbox”

The name of the attribute (attribute of the dojo.data.item) that specifies the items checkbox initial state.

Example: { name:’Egypt’, type:’country’, checkbox: true }

If a dojo.data.item has no ‘checkbox’ attribute specified it will depend on the attribute ‘checkboxAll’ if one will be created automatically and if so what the initial state will be as specified by ‘checkboxState’.

checkboxMState

String

“mixed”

The name of the attribute (attribute of the dojo.data/item) that will hold the checkbox mixed state. The mixed state is separate from the default checked/unchecked state of a checkbox.

checkboxRoot

Boolean

false

If true, the root node will receive a checkbox even though it’s not a true entry in the store.

This attribute is independent of the showRoot attribute of the Tree itself. If the Tree attribute ‘showRoot’ is set to false the checkbox for the root will not show either.

checkboxState

Boolean

false

The default value/state applied to every checkbox unless otherwise specified for the dojo.data item.

(see also: checkboxIdent)

checkboxStrict

Boolean

true

If true, a strict parent-child checkbox relation is maintained. For example, if all children are checked the parent will automatically be checked or if any of the children are unchecked the parent will be unchecked.

CheckBoxTree Attributes

Attribute

Type

Default

Description

branchIcons

Boolean

true

Determines if the FolderOpen/FolderClosed icon is displayed.

checkboxMultiState

Boolean

true

Determines if Multi State (mixed) checkbox behavior is required. If set to false the value property of a checkbox can only be ‘checked’ or ‘unchecked’. If true the value can be ‘checked’, ‘unchecked’ or ‘mixed’

checkboxStyle

String

“dijit”

Sets the style of the checkbox to be used. The default is “dijit” any other value will force the use of the native HTML style checkbox. The visual representation of a mixed state checkbox is only supported with a dijit style checkbox.

32 Comments

Hello, Excellent blog (this one and the previous one). I’m in the process of going through the previous blog ang getting ready to understand the implementation of this one as well. For my purposes, I have the following additonal requirements and wanted to get your take on how to implement them:
1. Root Node must have a slider control and no checkbox
2. Leaf Nodes of a certain type do not need a checkbox nor leaf image
3. All other leaf nodes need two checkboxes on them, the second using a different iconClass by which it does not appear to be a checkbox but still functions as one

1. I’m guessing I can do a quick check for the root and create a slider control
2. What are the gotchas for not having a checkbox on a node as far maintaining the parent chlid relationships, if no checkbox exist, should I return true when the node is being probed for it’s current state?
3.I’m guessing I can follow your multi state example on how to create my own image strip that I can then use as my icon class

First of all, I’m sorry about the very late response. I’m currently working on completely different stuff AND for some reason WordPress (my blog software) stopped notifying me of any new posts. Anyway, to answer your questions:

1. Root Node must have a slider control and no checkbox

This would be something you would have to do yourself. I would recommend using the ‘Multistate checkbox’ as an example. Not displaying a checkboxes is already configurable on a per node basis.

2. Leaf Nodes of a certain type do not need a checkbox nor leaf image

Both features are currently available using either the attributes during tree creation or by means of the json data file which will allow you to specify if a checkbox is required for each individual node.

3. All other leaf nodes need two checkboxes on them, the second using a different iconClass by which it does not appear to be a checkbox but still functions as one

Again, use the multi-state checkbox as an example there is quit a bit of documentation in the code that should help you to understand how things work.

2. What are the gotchas for not having a checkbox on a node as far maintaining the parent chlid relationships, if no checkbox exist, should I return true when the node is being probed for it’s current state

Please remember, the checkbox is only a visual representation of a state in the datastore. As an example: if you select the standard HTML checkbox you will only see ‘checked’ or ‘unchecked’ however, the datastore will still have the capability of maintaining all three states. In reallity this is one of the beauties of the Model-View-Controller concept. (in other words) there is no direct relation between what is maintained in the datastore and what is actually displayed. Therefore, not displaying a checkbox has no impact on the parent-child relationship.

Thanks for the post, very useful and great explanations.
I used you code to setup a checkbox tree but noticed poor performance toggling checkboxes when the number of nodes starts to get high (>200). Any idea on how to improve this?

Sorry to keep harping on the licensing question, but could you possibly declare the code to be under a recognized F/OSS license? Your comment “The software is freeware, no fee, use/modify it as you like but at the same time no warrenty or support.” is a little ambiguous because ‘freeware’ usually means ‘it doesn’t cost anything and can be freely redistributed but you can’t modify it’. I’d like to package something which uses this code (tt-rss) in Fedora and Fedora has strict licensing policies; the code has to be clearly under an accepted license. You could simply use the PHP License itself. Another good choice that represents your ‘use/modify it as you like’ idea is the BSD license – the “New BSD” and “2 clause BSD” variants at https://fedoraproject.org/wiki/Licensing/BSD are commonly used. All you’d have to do is put one of those licenses into the zip file for the code, or in the header of the file itself – that would probably be the best idea as the file is often stripped out and shipped on its own as part of someone else’s project, so if you put your name and the license in the header of the file, everyone would always know what terms they can use it under. thanks!

Very nice widget, as a newbie to dojo it has taught me a great deal just going through the code with a fine tooth firebug comb.

I seem to have everything working except the removal of branch and node icons.

I’m using a modified version of dojo 1.6 included in the ESRI javascript API, so that may be part of the issue. However, the standard tree works fine. But, if I add some comments in the _createCheckbox object:

when I examine the tree I see that branchIcons and nodeIcons are false (as I set in the tree parameters) and the code successfully removes “dijitTreeIcon” from this.iconNode in the img class statement. But, “dijitIcon” remains in the image class and the icon does not disappear:

What I’m really trying to do here is override the icon image using getIconClass:

This works successfully in the standard dijittree.html setup at the beginning of the tutorial, but I can’t get it to work when adding it to the checkboxtree implementation. I get but not the new override css class .iconImg or the dijitLeaf class. Eventually I want to store the image css class name in the JSON so that each leaf can have an individual image to create a legend.

Any suggestions you may have are appreciated.

There is another option, shown here http://robrobbins.info/?tag=dojo, and I think it would be possible to override the _TreeNode attribute map, but this is way over my programming head at this point.

Hello, nice widget. How can I submit the clicked values via form and secondly, in another form (edit), I would like to tick the nodes programmatically or have the widget ticked those nodes based on values from my json/database. Is this possible?

One problem though: using the multistate checkboxes, when I check/uncheck a child checkbox, the parent checkboxes do not refresh until I hover over them. When I hover, they refresh correctly. Any ideas?

How do I query the store for the list of checked nodes and display the whole checked nodes on the checkbox tree page??
Also I need to maintain a xml containing the list of the checked nodes and their children ?? Thanks in advance

This took me some time, too.
The solution is to add the following code to the “_setValueAttr” method:

// To update the visual state, _setStateClass has to be called.
// Unfortunately, _CssStateMixin indeed monitors a real crowd of
// attributes and calls _setStateClass automatically, but it does
// NOT monitor “value”.
this._setStateClass();

Could someone advise me how to disable some of the checkboxes from the model so that a user cannot check/uncheck that checkboxes. Disabled checkboxes should be visible (disabled state) but totally ignored by parent/s checkboxes (checked/unchecked/mixed)…

Firstly thank you for taking the time to document this so thoroughly. Your functionality is exactly what I need for my project.
The download code (Release date: 02/12/2010) works perfectly with Dojo 1.4.3
With 1.7.x the code fails:
1) branchIcons true/false doesn’t work
2) nodeIcons true/false doesn’t work
3) the update of the multistate dijit checkbox fails to refresh without ‘hovering’ over the check box

There are 3 ‘DEPRECEATED – will be removed in version: 2.0′ warnings in debug mode that will become future problems