The whole concept is based on hierarchic data in the first place. I started with Fritz Onion's sample from here. I added a couple of spans with classes so that the jQuery script can target:
- an entire hierarchical group (colorGroup) - one higher level box and all its lower level ones
- just the higher level box (colorGroupHeader)
- just the lower level boxes (color)
Like this:
<asp:DataList ID="DataList1" runat="server" DataSourceID="ObjectDataSource1">
<ItemTemplate>
<span class="colorGroup">
<span class="colorGroupHeader">
<asp:CheckBox ID="CheckBox1" runat="server"
Text='<%# DataBinder.Eval(Container.DataItem, "GroupName") %>' />
</span>
<span class="color">
<asp:CheckBoxList ID="SyndicateDropDownList2" runat="server"
DataSource='<%# DataBinder.Eval(Container.DataItem, "ColorNames") %>'
DataTextField="Name" DataValueField="Color">
</asp:CheckBoxList>
</span>
</span>
</ItemTemplate>
</asp:DataList>
and finally I added this jQuery script (assumes you already have a link to the jQuery library in the page)
$(document).ready(function() {
// When the user clicks on the colorGroupHeader input box cascade that to the
// Scheme input boxes lower down
$(".colorGroupHeader").find("input").click(function() {
var isChecked = $(this).attr("checked") == "checked";
$(this)
.parents(".colorGroup")
.first()
.find(".color")
.find("input")
.attr("checked", isChecked);
});
// When the user clicks a color input box push that UP to the colorGroup above.
// The colorGroup reflects if all the lower boxes are checked, else it is unchecked
$(".color").find("input").click(function() {
var siblingInputElements = $(this).parents(".color").find("input");
var uncheckedSiblings = siblingInputElements.filter(function() {
var state = !($(this).attr("checked") == "checked");
return !($(this).attr("checked") == "checked");
});
var allSiblingsAreChecked = uncheckedSiblings.length == 0;
$(this)
.parents(".colorGroup")
.first()
.find(".colorGroupHeader")
.find("input")
.attr("checked", allSiblingsAreChecked);
});
});