Tri-State Check Boxes in jQuery TreeView

Created: 15 September 2014

Normally each item in jQuery TreeView is shown using a label and an icon. We can modify the item appearances using templates which may contain custom HTML elements arranged in custom layouts. For the purpose of this article we will show you how to add a check box to each item and placed in before its label. Also we will show you how to update the value of parent and child check boxes when a specific item check box is clicked.

As you may notice from above tree structure, we are using the content property to add a custom HTML generated content to each item. This is required if we want to create items with custom elements arranged in different layouts than default one.

Now when we have our sample data, we can populate the TreeView with it. For this purpose we will be using the following function:

Whenever this function is called, at first will clear the current content of TreeView prior adding any new items. We are using the clearItems method to empty the TreeView and addItem method to add new items to it.

When we refresh our web page at this stage, the TreeView will be shown with all items containing a check box and a label. However, when check boxes are clicked, the values of other check boxes above and below will remain the same, that is unchecked.

We need to use jQuery to change the check box value to one of three states: checked, indeterminate and unchecked. The indeterminate value cannot be set by changing the checked attribute value of check box element. It can only be set by using the jQuery prop method:

As it can be seen from above code, to make a check box with indeterminate value, we need to use the inderminate property of checkbox element. However, when this property is set to true, it may cause problems when checkbox is checked or unchecked. To make sure this works in those cases, we need to set the indeterminate property value to false.

Whenever a checkbox value is changed, we need to update the values of parent and child check boxes. To update the parent check boxes, we need to create a loop which determines all parent items for our clicked item:

To locate the parent item we are using the getItemParent method. This method returns the found parent of specified item or if there is no parent a null value. By cycling through all parent items we are checking how many items have their check box checked. If all items have their check boxes checked, that means that the parent item should also have its checkbox value set to checked. If some items have check boxes with indeterminate value, the parent check box will also have the indeterminate value. Finally if all child items have unchecked checkboxes, the parent check box will also become unchecked.

In similar way we can cycle to all child items for our current item, and change the values of all child check boxes. We need to do this, because when parent check box changes its value to checked or unchecked, this change must also reflect all child check boxes to have their value set to checked or unchecked respectively. Here is the code:

Because each checkbox element contains a custom HTML5 attribute named data-element with value set to checkbox, we can easily locate it using the jQuery attribute selector. To make sure that only the first found checkbox element is selected, we are using the eq method.

When we have a reference to the element in the DOM, we can change its value using the jQuery prop method, like it is shown in above code.

The code so far was for changing the value of check box elements depending on checkState of corresponding items. We also need to allow changes to take place whenever a check box is clicked. To do this , we need to handle the change event for all check box elements:

// Update the check box value to match the checkState value of their corresponding item

for (var i = 0; i < treeList.length; i++){

updateCheckBox(treeList[i]);

updateParentItem(treeList[i]);

}

// Handle the change event for all check boxes

$tree.find("[data-element='checkbox']").each(function(index){

var $checkBox = $(this);

$checkBox

.off("change click")

.on({

change: function(){

var currentValue = $(this).prop('checked');

if (index >= 0 && index < treeList.length){

var item = treeList[index];

if (item){

item.checkState = currentValue ? 'checked' : 'unchecked';

updateChildItem(item);

updateParentItem(item);

}

}

},

click: function(e){

e.stopPropagation();

}

});

});

}

Finally when tree item is expanded or collapsed all child elements are removed from the DOM. This will also remove the change event from any check box in child items. To make sure our code works in all cases, we will handle the aftercollapse and afterexpand events and rebind the change event and update the check boxes with value equal to the checkState of the item to which they belong.

// Handle the expand and collapse events so that check boxes are refreshed

$tree.on({

"aftercollapse": function(e){

refreshCheckBoxes();

},

"afterexpand": function(e){

refreshCheckBoxes();

},

"itemclick": function(e){

// Get the item that is clicked and update the value of all affected check boxes

if (e.object){

if (e.object.checkState === 'checked')

e.object.checkState = 'unchecked';

else

e.object.checkState = 'checked';

// Update the value of item checkbox element

updateCheckBox(e.object);

// Update the value of all child items

updateChildItem(e.object);

// Update the value of all parent items

updateParentItem(e.object);

}

}

});

As it can be seen from above code, we are also handling the itemclick event, to allow changes to the check boxes whenever item label is clicked.

A live demonstration above shows how all code in this article works. Whenever a check box is clicked, it triggers changes to all affected parent and child check boxes.

Tab content switching

Newsletter

Sign-up to our newsletter and you will receive news on upcoming events, latest articles, samples and special offers.

Name:

Email: *

*By checking this box, I agree to receive a newsletter from Lidor Systems in accordance with the Privacy Policy. I understand that I can unsubscribe from these communications at any time by clicking on the unsubscribe link in all emails.