jQuery Selectors (Form and Hierarchy)
After discussing
Attribute selectors and many filters in the previous part let's move ahead
and understand the remaining couple of selectors. So far, you know Basic
selectors, Basic filters, Attribute selectors, Child filters, Content filters
and Visibility filters of jQuery. In this article I cover the remaining
selectors viz. Form selectors and Hierarchy selectors.
Understanding Form Selectors
Form selectors allow you to select FORM elements based on their type
(textbox, checkbox, radio button etc.) or their status (selected, checked,
disabled etc.). The following table list all the available Form selectors.
Form selector |
Description |
:input |
Selects all elements of type input, textarea, select and button |
:text |
Selects all <INPUT> elements whose type is text |
:password |
Selects all <INPUT> elements whose type is password |
:button |
Selects all <INPUT> elements whose type is button as well as button
elements |
:submit |
Selects all <INPUT> elements whose type is submit |
:reset |
Selects all <INPUT> elements whose type is reset |
:image |
Selects all <INPUT> elements whose type is image |
:checkbox |
Selects all <INPUT> elements whose type is checkbox |
:radio |
Selects all <INPUT> elements whose type is radio |
:file |
Selects all <INPUT> elements whose type is file |
:checked |
Selects all checkboxes and radio buttons that are checked |
:selected |
Selects all <OPTION> elements of <SELECT> element that are selected |
:enabled |
Selects all elements that are enabled |
:disabled |
Selects all elements that are disabled |
In order to understand how Form selectors are used you will develop a web
form as shown below:
The web form consists of a GridView that displays Employees table of
Northwind database. The first two columns of the GridView are template fields.
The first one contains checkboxes whereas the second one contains radio buttons.
The checkbox in the header allows you to select/unselect all the checkboxes. The
second column allows you to select just one radio button out of all the
available ones. If you ever used radio buttons inside a GridView you are
probably aware that they don't work as a single group because GridView generates
different "name" for all of them. Here you will see how jQuery can be used to
tackle that problem.
At the top there is a ListBox that allows you to select one or more cities.
The GirdView then enables checkboxes and radio buttons only for those records
and they are highlighted with some CSS class. The Clear selection button below
the ListBox allows you to clear selection made in the ListBox so that GridView
again shows all the records have checkboxes and radio buttons enabled.
Now let's discuss the jQuery code that makes it work.
$(document).ready(function() {
$("#GridView1 :checkbox[id$='CheckBox2']").change(function() {
if ($("#GridView1 :checked[id$='CheckBox2']").is(":checked")) {
$("#GridView1 :checkbox[id$='CheckBox1']").attr("checked", "checked");
}
else {
$("#GridView1 :checkbox[id$='CheckBox1']").removeAttr("checked");
}
})
,
$("#GridView1 :radio[id$='RadioButton1']").change(function() {
var newId = this.id;
$("#GridView1 :radio[id$='RadioButton1']").each(function(index) {
if (this.id != newId) {
$(this).removeAttr("checked");
}
})
})
,
$("#ListBox1").change(function() {
$("#GridView1 :input").attr("disabled", "disabled");
$("#GridView1 input:disabled").removeClass("Focus");
$("#ListBox1 option:selected").each(function() {
$("#GridView1 tr:contains('" + this.value + "')").each(function() {
$(":input", this).removeAttr("disabled");
})
})
$("#GridView1 input:enabled").addClass("Focus");
})
,
$("#Button1").click(function(event) {
$("#ListBox1 option").each(function() {
$(this).removeAttr("selected");
})
$("#GridView1 :input").removeAttr("disabled");
$("#GridView1 :input").removeClass("Focus");
event.preventDefault();
})
})
First we wire the change event handler for the header checkbox. To select the
header checkbox you use checkbox Form selector as follows:
$("#GridView1 :checkbox[id$='CheckBox2']").change(function() {
if ($("#GridView1 :checked[id$='CheckBox2']").is(":checked")) {
$("#GridView1 :checkbox[id$='CheckBox1']").attr("checked", "checked");
}
else {
$("#GridView1 :checkbox[id$='CheckBox1']").attr("checked", "");
}
})
The :checkbox selector will return all the checkboxes from GridView1 but we
want to deal with only the header checkbox. So, you need to use Ends With
Attribute selector. The reason we need to use Ends With Attribute selector is
that GridView changes the IDs of its child controls. For example, when I created
this web form the checkbox in the header template was having ID CheckBox2.
However, it is rendered as GridView1_ctl01_CheckBox2. Obviously "equal to"
comparison will fail. Hence we use $= to get hold of the header checkbox. The
change event handler then checks if the header checkbox is checked using is()
method. If the checkbox is checked then we set checked attribute of all the
checkboxes to checked so that all of them are checked. Otherwise, we clear that
attribute so that they are unchecked.
$("#GridView1 :radio[id$='RadioButton1']").change(function() {
var newId = this.id;
$("#GridView1 :radio[id$='RadioButton1']").each(function(index) {
if (this.id != newId) {
$(this).attr("checked", "");
}
})
})
We then wire change event handler to all the radio buttons. Unlike checkbox,
where we handled change event of header checkbox only, we need to handle change
event on all the radio buttons because we need to ensure that only one is
selected at a time. This time we use :radio form selector and then use Ends With
Attribute selector ($=) to select all the radio buttons that end with
RadioButton1. The ID of current radio button (i.e. the one checked by the user)
is stored in a variable (newId). We then iterate through all the radio buttons
and with each iteration we check if ID of that radio button matches with the one
stored in newId variable. If they don't we just uncheck the radio button. Notice
the use of each() method that iterates through all the elements returned by the
selector. The code specified in the each() is executed for each element and for
every iteration "this" refers to the element being iterated.
$("#ListBox1").change(function() {
$("#GridView1 :input").attr("disabled", "disabled");
$("#GridView1 input:disabled").removeClass("Focus");
$("#ListBox1 option:selected").each(function() {
$("#GridView1 tr:contains('" + this.value + "')").each(function() {
$(":input", this).removeAttr("disabled");
})
})
$("#GridView1 input:enabled").addClass("Focus");
})
Next, we handle change event of ListBox control. Remember that ListBox
control of ASP.NET gets rendered as <SELECT> element. We first get hold of all
the checkboxes and radio buttons using input form selector (you could have also
used :checkbox and :radio separately) and we disable them by setting their
disabled attribute. We then remove Focus CSS class (shown below) from all the
disabled elements. For each selected option from the ListBox we execute code as
specified in the each() method. The code checks if the selected city is present
in any of the GridView rows. This is done using :contains() content selector. If
any row is found, checkbox and radio button from that row are enabled by
clearing previously set disabled attribute. For all the enabled checkboxes and
radio buttons a CSS class named Focus is applied. The Focus CSS class is shown
below :
.Focus
{
background-color:orange;
border:solid 2px red;
}
Finally we handle click event of the button that clears the ListBox
selection.
$("#Button1").click(function(event) {
$("#ListBox1 option").each(function() {
$(this).removeAttr("selected");
})
$("#GridView1 :input").removeAttr("disabled");
$("#GridView1 :input").removeClass("Focus");
event.preventDefault();
})
The click event handler iterates through all the option elements of the
ListBox1 and removes their selected attribute. Clearing selection from the
ListBox means the grid should allow selection of all the rows. So we remove
disabled attribute from all the checkboxes and radio buttons. Focus CSS class is
also removed.
If you are developing data entry web forms, Form selectors will be very handy
and you will be using them frequently.
Now let's move ahead and learn Hierarchy selectors.
Understanding Hierarchy Selectors
As the name suggests Hierarchy selectors allow you to work with child and
sibling elements. You might be surprised but you already used one of the
Hierarchy selector many times. Whenever you used $("#GridView1 something")
you were actually using Descendant selector. The following table lists all the
four selectors available in this category :
Hierarchy Selector |
Operator |
Description |
Child selector |
> |
Selects all the direct child elements of a given element. |
Descendant selector |
white space |
Selects all the direct as well as indirect child elements of a given
element. Indirect child elements means grandchild, grand-grandchild etc. |
Next Adjacent selector |
+ |
Selects next sibling element that is immediately following a given
element. |
Next siblings selector |
~ |
Selects all the next sibling elements of a given element. |
These selectors are best understood with an example. So let's develop one.
See the web form below that represents a typical data entry page accepting
details such as first name, last name and address.
The web form consists of six Panel controls in all. The top panels with navy
border, three with red border and one that acts as a container for this whole
page and is invisible in the figure. The panels and textboxes get their borders
and colors via jQuery code.
$(document).ready(function() {
$("#Container > div").css("border", "solid navy 2px");
$("#Panel2 fieldset > div").css("border", "solid red 2px").css("margin", "10px");
$("span + :text").css("background-color", "silver").css("border", "solid gray 1px");
$("span ~ :text").css("font-family", "courier");
})
The container panel has ID set to Container. The first line selects all the
direct child div elements of this container panel. Remember that ASP.NET Panel
control is rendered as <DIV> element in the browser. Since we want direct child
div elements we used > operator. This child selector gives us two panels (basic
details, contact information) and we then set their border color to navy. To get
hold of panels that are inside Panel2 (contact information) we again use child
selector in combination with descendant selector. We have set GroupingText
property of all the panels to some meaningful text (Basic Details, Contact
Information and so on). When you set the GroupingText property ASP.NET emits <fieldset>
and <legend> elements as shown below:
<div id="Panel2" style="text-align:center">
<fieldset>
<legend>
Contact Information
</legend>
<div id="Panel3" style="width:300px;">
...
So Panel3, Panel4 and Panel5 are not direct child elements of Panel2. They
are child elements of <fieldset> element. Hence, we need to use fieldset element
in the selector. Once selected we set their border to red.
Then we use Next Adjacent selector to select all the textboxes. In our web
form Label controls and textboxes are placed side by side. If you wish to select
all the textboxes that are immediately next to Labels then "span + :text" will
do the job. The "span + :text" selector simply means "get me all the textboxes
(:text) that are placed immediately after Labels (span)". Notice that since we
used Next Adjacent selector the textarea won't get selected because there is a
line brake (<br />) between Landmarks label and the textarea i.e. they are not
adjacent. So "span + :text" selector returns six textboxes (two from Panel1 and
four from Panel3). We then set the background color for these textboxes to
silver and border color to gray.
Finally, we used Next Siblings selector "span ~ :text". This selector simply
means "get me all the textboxes (:text) that are anywhere after the Labels
(span)". Since Next Siblings selector selects all the next siblings of an
element this time even the textarea will be selected. We then set font-family of
all the textboxes to courier.
That's it! We just completed all the selectors of jQuery. In the next part I
will explain how the HTML page elements can be manipulated using jQuery. Stay
tuned!