Update: The javascript on this blog has been suppressed by the blogger's parser. Consequently, some of the functionality may not work. Inconvenience is regretted.

Monday, June 4, 2007

Asp.Net 2.0 Treeview Checkbox Check/Uncheck All script

Its a very common requirement to have the parent-child check behaviour in asp.net treeview. To define the problem:
1)Check all the child nodes if the parent is checked and uncheck all child nodes if parent is unchecked ( well, this part is simple).
2)If a node at any level is checked and all its siblings are already checked then the parent node should be checked and the same should apply for the parent node(i.e., if its siblings are checked....), this should happen till the root node.
3)If a node at any level is unchecked then the parents( ma, grand ma, grand grand ma....) up to the root node must be unchecked.

Well there have been scripts on the net that only half accomplished the task(check footnotes). So I wrote the script that solves the problem completely, upto best of my knowledge. I've tested in IE 7 and Firefox 2.0, hope it works fine for you all.

Here's how to implement it:

Step 1: In the page load in code behind file add an attribute to the treeview as:

If(!isPostBack)
{
    TreeView1.Attributes.Add("onclick","OnTreeClick(event)");
}

The desired affect could also be accomplished by direclty adding the attribute to the treeview tag in .aspx file as: <asp:treeview onclick="OnTreeClick(event)"... which would cause Visual Studio to display a warning but it works anyway but the codebehind way of doint it is the right way.

Step 2: Put the below script in the head section of your .aspx page:

Oops!! The current security settings of your browser do not allow the scripts on the page to access your clipboard.If you are using mozilla firefox, you may change security settings as follows: Type about:config in the url bar and set signed.applets.codebase_principal_support to true. For more info, check here or contact blog author.
 

function OnTreeClick(evt)
{
var src = window.event != window.undefined ? window.event.srcElement : evt.target;
var isChkBoxClick = (src.tagName.toLowerCase() == "input" && src.type == "checkbox");
if(isChkBoxClick)
{
var parentTable = GetParentByTagName("table", src);
var nxtSibling = parentTable.nextSibling;
//check if nxt sibling is not null & is an element node
if(nxtSibling && nxtSibling.nodeType == 1)
{
if(nxtSibling.tagName.toLowerCase() == "div") //if node has children
{
//check or uncheck children at all levels
CheckUncheckChildren(parentTable.nextSibling, src.checked);
}
}
//check or uncheck parents at all levels
CheckUncheckParents(src, src.checked);
}
}

function CheckUncheckChildren(childContainer, check)
{
var childChkBoxes = childContainer.getElementsByTagName("input");
var childChkBoxCount = childChkBoxes.length;
for(var i=0;i<childChkBoxCount;i++)
{
childChkBoxes[i].checked = check;
}
}

function CheckUncheckParents(srcChild, check)
{
var parentDiv = GetParentByTagName("div", srcChild);
var parentNodeTable = parentDiv.previousSibling;
if(parentNodeTable)
{
var checkUncheckSwitch;
if(check) //checkbox checked
{
var isAllSiblingsChecked = AreAllSiblingsChecked(srcChild);
if(isAllSiblingsChecked)
checkUncheckSwitch = true;
else
return; //do not need to check parent if any(one or more) child not checked
}
else //checkbox unchecked
{
checkUncheckSwitch = false;
}

var inpElemsInParentTable = parentNodeTable.getElementsByTagName("input");
if(inpElemsInParentTable.length > 0)
{
var parentNodeChkBox = inpElemsInParentTable[0];
parentNodeChkBox.checked = checkUncheckSwitch;
//do the same recursively
CheckUncheckParents(parentNodeChkBox, checkUncheckSwitch);
}
}
}

function AreAllSiblingsChecked(chkBox)
{
var parentDiv = GetParentByTagName("div", chkBox);
var childCount = parentDiv.childNodes.length;
for(var i=0;i<childCount;i++)
{
if(parentDiv.childNodes[i].nodeType == 1)
{
//check if the child node is an element node
if(parentDiv.childNodes[i].tagName.toLowerCase() == "table")
{
var prevChkBox = parentDiv.childNodes[i].getElementsByTagName("input")[0];
//if any of sibling nodes are not checked, return false
if(!prevChkBox.checked)
{
return false;
}
}
}
}
return true;
}

//utility function to get the container of an element by tagname
function GetParentByTagName(parentTagName, childElementObj)
{
var parent = childElementObj.parentNode;
while(parent.tagName.toLowerCase() != parentTagName.toLowerCase())
{
parent = parent.parentNode;
}
return parent;
}


Get Formatted Version Of Above Script

The script is pretty much self explanatory with function names saying it all.
Comments awaited.

Footnotes: There has been other scripts on the net that accomplished the job only partially, the one that reached closest to doing it could be found in this thread on asp.net forums, i too have posted my solution there.

65 comments:

Anonymous said...

thanks very good piece of code, this yours treeView works fine, but I have one question:

Where I could change a little bit your code, I need to have that if child node is selected then automatically parient node is selected. Now it is like if all childes nodes are selected then parient node is selected automatically.

thanks in advance.

Pushpendra said...

Hi,

Try commenting all code in the function AreAllSiblingsChecked keeping the lastline that returns true, so effectively that function should always return true.

Hope that helps.

pushp

Anonymous said...

Your code saved my life...

but one quick question.

Checking parent node A doesn't seem to check A's child nodes unless the node A has been expanded previously at least once..

is there a way to check all nodes by checking the highest parents without exapanding at all?

Please let me know.

PS: if you didn't understand my Q, I will be more than happy to explain again. :)

Pushpendra said...

Hi,

It works fine even if all nodes are collapsed. I think you are using populate on demand for the treenodes. Please clarify a bit more.

Pushp

Anonymous said...

solution works a charm. TreeView doesn't have postback, and I want the changes to happen immediately, but it only seems to happen after it's dona a trip to the server. I use Ajax, but still..it only posts if I click on the text of the node and not after I've checked the actualt checkbox... :(

Any ideas? I have an invisible button in my updatepanel to initiate the postback, but when?! Sorry..Im such a noob!

Pushpendra said...

Hi Dvius,

I think you need a postback on the check of the checkbox.

If so, you just need to click your hidden button in the OnTreeClick function:
----------------------
function OnTreeClick(evt){
.............
CheckUncheckParents(src, src.checked);

//click your hidden button here
document.getElementById('btnId').click();
}

-------------------

In case you need to send some data to server, just put a hidden field inside the update panel and set its value appropriately before clicking your button.

Hope that helps.

pushp

Unknown said...

Works fine! This is a nice piece of code that most people with a treeview can find very useful :)

Sri said...

Useful code. Thanks to the author.

Anonymous said...

Thanks! Helped me a lot. Works great.

Anonymous said...

Hi, thank you for sharing this very useful bit of code. It has everything I want but for one thing. I am trying to have the functionality in such a way that when a child node is checked all it's parent nodes are checked too. Right now it is only doing the other way i.e. children being checked when a parent node is checked. Any help in this regard is greatly appreciated.

Pushpendra said...

Hi,

Your problem has already been answered if you check my reply to the second comment above. Try commenting all code in the function AreAllSiblingsChecked keeping the last line that returns true. This should give you the desired functionality.

Hope this helps.

Anonymous said...

Thank you for this wonderful piece of code. Its exactly what I was looking for. But I wanted to ask about implementing the following functionality:
When a Parent node is checked, NONE of its child nodes should gets checked. But when a child node is checked/unchecked, all its parents and grand parents should gets checked/unchecked. Any answer in this regard would be highly appreciated.

Anonymous said...

Beautiful!! Saved lot of time for me!! God bless you.

Anonymous said...

Had a question along with my previous comment. How to change the style of checked/unchecked checkboxes? My requirement is to make it bold when checked and not bold when unchecked.. is it possible? Thanks

Gaurav said...

Wonderful piece of code. I need one more thing. Right now On selecting Parent Node All child Nodes are getting selected . I want that parent node should also expand alongwith that and vice versa. Any help please..

marc said...

hi,

splendid code, one question though is there a way where i click the the node name not the check box and the check boxes gets checked?

keep it up.

Pushpendra said...

Hi 'Anonymous',

To answer your first question:

"When a Parent node is checked, NONE of its child nodes should gets checked. But when a child node is checked/unchecked, all its parents and grand parents should gets checked/unchecked"

You just have to comment the call to CheckUncheckChildren in the OnTreeClick function.

For your next question regarding applying styles to nodes on checkbox click, just include the below two lines in your OnTreeClick function as:

if (isChkBoxClick)
{
var node = GetNextSibling(src);
node.style.fontWeight = src.checked ? "Bold" : "Normal";


I've used the following helper method:

function GetNextSibling(currentObject)
{
var nxt = currentObject;
do nxt = nxt.nextSibling;
while (nxt && nxt.nodeType != 1);
return nxt;
}


That should do your job.

Hope this helps.

PS: Please do not post questions anonymously as it becomes difficult to specify whose query is being answered. Anonymous appreciation and criticism is welcome though :)

Pushpendra said...

Hi Gaurav,

Please take the idea from my other post here http://pushpontech.blogspot.com/2007/06/client-side-expandcollapse-all-nodes.html

TreeView_ToggleNode function is your man. Just call it appropriately on the node getting axpanded or collapsed.

Hope this helps.

Anonymous said...

Hi Pushp,

Thank you SO much for creating this wonderful piece. You have helped many people like me. I truly appreciate you take your time to create and answer other people questions as well. Because your answers also helped me what I looked for. THANK YOU!!! You are Super.

Thanks again!

Angie

Anonymous said...

Hi Pushp,

Is there a way that I can validate if at least a tree node is checked? Thank you VERY much in advance.

Thanks,

Angie

Pushpendra said...

Hi Mark,

You can modify the OnTreeClick function as below to have the checkbox check effect on node click (uncheck is not implemented but can be done without much effort):

function OnTreeClick(evt)
{
var src = window.event != window.undefined ? window.event.srcElement : evt.target;
var isChkBoxClick = (src.tagName.toLowerCase() == "input" && src.type == "checkbox");
var isNodeClick = (src.tagName.toLowerCase() == "a");
if(isNodeClick)
{
isChkBoxClick = true;
src = GetPreviousSibling(src);
src.checked = true;
}


if(isChkBoxClick)
{
..............

Here is the helper function:

function GetPreviousSibling(currentObject)
{
var prev = currentObject;
do prev = prev.previousSibling;
while (prev && prev.nodeType != 1);
return prev;
}


Hope this helps.

Pushpendra said...

Hi Angie,

You can use this little function to know if atleast one node is checked:


function IsAnyNodeChecked(treeViewID)
{
var inputsInTree = document.getElementById(treeViewID).getElementsByTagName("input");
for(var i = 0; i < inputsInTree.length ; i++)
{
var currentElement = inputsInTree[i];
if(currentElement.type == "checkbox" && currentElement.checked)
return true;
}
return false;
}


Do not forget to pass the appropriate treeview id to function.

Thanks angie for your appreciation. Its really heartening to know that my posts are helpful to you guys.

Anonymous said...

Thank you SO much, Pushp. I truly appreciate that you take your time to help me and others. THANK YOU VERY MUCH!!!!

Nuno Mota said...

Hi Pushp,

Where i can call the function IsAnyNodeChecked in your code ??

function IsAnyNodeChecked(treeViewID)
{
var inputsInTree = document.getElementById(treeViewID).getElementsByTagName("input");
for(var i = 0; i < inputsInTree.length ; i++)
{
var currentElement = inputsInTree[i];
if(currentElement.type == "checkbox" && currentElement.checked)
return true;
}
return false;
}


Thank you very much...

Pushpendra said...

Hi Nuno,

You can use this script whereever you need to check the treeview for any checked nodes. I'm not sure why would you use it in my scripts but if you need to use it therein, just pass/get the correct treeview id and use it.

HTH
pushp

Unknown said...

And i have question
is it possible to do checkbox all and uncheckbox all with the same fonction with the treeview ID ?

Pushpendra said...

Why not? You could've figured it out just by looking at IsAnyNodeChecked function above. Anyways, try this:

function CheckUnCheckAll(treeViewID, checkAll)
{
var inputsInTree = document.getElementById(treeViewID).getElementsByTagName("input");
for(var i = 0; i < inputsInTree.length ; i++)
{
var currentElement = inputsInTree[i];
if(currentElement.type == "checkbox")
currentElement.checked = checkAll;
}
}

Just pass checkAll as true/false.

HTH,
pushp

sanju said...

It's realy very good i like it ,

But i need to modify code ,
i want parent node Check, till i not unchecked all subling ,if any subling remain checked ,parent is also checked.

Anonymous said...

TreeView1.Attributes.Add("onclick","OnTreeClick(event)");
does not work in firefox. please suggest.

Anonymous said...

If you are using

TreeView1.Attributes.Add("onclick","OnTreeClick(event)");

and if its not working in Firefox then make sure your script tag does not contain
[ type="text/jscript" ]

script type="text/jscript" language="javascript"

Remove [ type="text/jscript" ]
it will start working

Anonymous said...

Hi

This a great code, It really helps me, you're great!

I have a question:

If I want for example that parent node remains checked, even if there's one child checked. Only If I uncheck all sublings, parent is unchecked

Thank you for your help

Pushpendra said...

Hi Leonhardo,

All you need to do is this:

1) Write a new method as:

-------------------------------
function IsAnySiblingChecked(chkBox) {
var parentDiv = GetParentByTagName("div", chkBox);
var childCount = parentDiv.childNodes.length;
for (var i = 0; i < childCount; i++) {
if (parentDiv.childNodes[i].nodeType == 1) {
//check if the child node is an element node
if (parentDiv.childNodes[i].tagName.toLowerCase() == "table") {
var prevChkBox = parentDiv.childNodes[i].getElementsByTagName("input")[0];
//if any of sibling node is checked, return true
if (prevChkBox.checked) {
return true;
}
}
}
}
return false;
}
-------------------------------
which is nothing but a minute variation over already existing 'AreAllSiblingsChecked' method.

2) Modify one line in 'CheckUncheckParents'

else //checkbox unchecked
{
checkUncheckSwitch = false;
}

as

else //checkbox unchecked
{
checkUncheckSwitch = IsAnySiblingChecked(srcChild);
}

that is it.

Hope this is what you need.

This post is getting pretty exhaustive on treeview checkbox scripting :-).

pushp

Anonymous said...

Well written article.

Rohit Sakalle said...

Hey Man,

Thanks a lot. Exaclty what I wanted for my requierment. What else we have many other codes on net but they don't work I found this one working cool. Now i will try to undertand what you have done.

Thanks again

Anonymous said...

Hello Pusph:

I want to stress in some way nodes father when one of the childs, grandchilds... nodes has been checked.

Can you help me?

Thank you very much

Unknown said...

Man, thank you very much.
You ROCK!!

Sandeep said...

Hi,

Great code...Thanks for sharing it..

Can anyone please help me how to put radio button in child nodes instead of checkbox?

Thanks in advance....

Sandeep said...

If radio button is not possible then how the behaviour of the checkbox can be made like radio button( for few parent nodes, not all)

Thanks in advance..

Pushpendra said...

Hi Sandeep,

These may help you:

1) http://fredrik.nsquared2.com/viewpost.aspx?PostID=299

2) http://forums.asp.net/t/1257589.aspx

Sandeep said...

Hi Pushp,

Thanks for the reply.

The second link seems like, perfectly matches my requirement...

Thanks a lot buddy...

You really rocks..

Thanks,
Sandeep

Sandeep said...

Hi,

How can we populate a message if more than one child node is seleted?

Thanks in advance..

Sandeep

Kailas S. said...

Thanks PUSHP,

This code is very helpful.

Pushpendra said...

Hi Sandeep,

Checkout my comment some 20 comments above your comment where I showed a function called IsAnyNodeChecked, you can modify that function (and possibly call it on every node check) and get your job done.

Hint: Put a global counter and increment it on every check and show the message as soon as it becomes greater than 1.

Zubair Ahmed said...

exhilarating piece of code thanks alot.

aravi said...

I have several checkboxes. when a user checks a few, how to get the value (text) of the check box?

GirafeT said...

Hello,

thank's a lot for your amazing code!

I am using PopulateOnDemand, and I would like too know if it's ppossible to expand a node when automaticaly checked (in order to be populated)?

I have to comment a line in the function AreAllSiblingsChecked, if (!prevChkBox.checked) {}. I think it's linked!
I precise that the top root of my treeview doesn't have a check box.

Thank's to you if you take time to answer me

Pushpendra said...

Hi GirafeT,

It is possible to automatically expand a node on checking it. Just get hold of the '+' link and click it on checkbox check event (will have to use previousSibling etc).

You can get the idea from here:
http://pushpontech.blogspot.com/2007/06/client-side-expandcollapse-all-nodes.html

HTH,
Pushp

Unknown said...

Sometimes not all your nodes will have checkboxes (since the TreeView allows you to change the ShowCheckBox property on individual TreeNodes). In that case you'll get an error in the AreAllSiblingsChecked() javascript function when you click a checkbox having sibling nodes with no checkbox.
Fix: just make sure getElementsByTagName("input") returns something before using it's first element:

function AreAllSiblingsChecked(chkBox) {
var parentDiv = GetParentByTagName("div", chkBox);
var childCount = parentDiv.childNodes.length;
for (var i = 0; i < childCount; i++) {
if (parentDiv.childNodes[i].nodeType == 1) {
//check if the child node is an element node
if (parentDiv.childNodes[i].tagName.toLowerCase() == "table") {
var inputsElements = parentDiv.childNodes[i].getElementsByTagName("input")
// this node might not have a checkbox
if (inputsElements.length > 0) {
var prevChkBox = inputsElements[0];
//if any of sibling nodes are not checked, return false
if (!prevChkBox.checked) {
return false;
}
}
}
}
}
return true;
}

Anonymous said...

Thanks a lot
Pushp
U saved me after searching internet i finally got the solution

Anonymous said...

Thanks dude..

works like a charm :-)

the gadget man said...

Awesome code man, works perfectly and so easy to apply.
Also, great that it's done clientside and not serverside :)

Thanks!

Anonymous said...

Great piece of code. Thank you very much.!

Anonymous said...

Good work yarr..

FRANCISCO CURIQUEO said...

Hi Pushp

Your examples are very good and very helpful.

Can you help me with the following problem

selections need to give a parent node and child nodes or deselect when selecting a child node to remove the selection or checket, I have the following groups

example, if left unchecked region select the parents of this (holding, chain) and the children of this (area, shop)

holding (parent)

chain (holding child node)
.
.
.
region (child node chain)
.
.
.
area (child node region)
.
.
.
store (child node area)
.
.
.

excuse my English because I speak Spanish

Pushpendra said...

Hi Francisco,

I probably need a bit more clarity on you requirement. Do you want the parent at any level checked if ANY of the grand children and so on are checked?

FRANCISCO CURIQUEO said...

Sorry for not clearly define my problem, I will explain my problem again
What I need is to select one of the five nodes in a hierarchical order, which is this
Holding
Chain
Region
Geographical area
Shop
In the tree there are several regions Chain and within Region and there are several areas within the area there are many shops, do I get data from a database (BD) of a commercial area X.
But I need when selecting region deselect Holding, chain stores area and all if they are selected, since at the time of making a filter that allows only bring the Regions and not the parent and children nodes have this node region, and so on both Holding, chain.
I hope you understand my problem, I appreciate your answer I see you like to share your knowledge and solve problems of people like me are complex.
I'm very complicated and I need to do this work for my review title, I hope you can help me and THANK YOU

Pushpendra said...

Okay so this is what I clearly understand: You have five nodes at root level and if I check any one the rest should get unchecked.

My question now is: Suppose I check 'Chain' then what happens to its children and if I check/uncheck any of the children of 'Chain' does it affect 'Chain' node.

Your problem is slightly confusing to me. You can explain your problem completely giving screenshots and in a bit more detail by sending me a mail here: mailpushp@gmail.com and I will be happy to help.

Anonymous said...

Sweet web site, I had not come across pushpontech.blogspot.com before during my searches!
Continue the superb work!

Anonymous said...

Hi there,

This is a question for the webmaster/admin here at pushpontech.blogspot.com.

Can I use part of the information from this blog post above if I provide a backlink back to your site?

Thanks,
Thomas

Pushpendra said...

Yes, Thomas you can.

Anil said...

thanks man....It worked well for me..........

Anushree said...

I had tried your code..and it worked fine but now my question is if i want to check tree on load all the check uncheck condition then how can i achieve that.

pushpendra said...

I'm surprised this post is still going strong :).

@Anushree - If I understand your question correctly, you want to check all nodes on the tree load (when the page loads). For that you can find the root check box on window.onload and check it programmatically. The script will do the needed cascading effect.

- pushp

Anonymous said...

Thanks dude, that snippet is really helpful and saved me a lot of work! May I please use it for my current project?

pushpendra said...

Sure, its free (as in beer) to use :)