Online courses in ASP.NET MVC / Core, jQuery, Angular, and Design Patterns conducted by Bipin Joshi. Read more...
Learn ASP.NET MVC / Core, jQuery, Angular, and Design Patterns through our online training programs. Courses conducted by Bipin Joshi on weekends. Read more details here.

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!

 




Bipin Joshi is a software consultant, trainer, author and a yogi having 21+ years of experience in software development. He conducts online courses in ASP.NET MVC / Core, jQuery, AngularJS, and Design Patterns. He is a published author and has authored or co-authored books for Apress and Wrox press. Having embraced Yoga way of life he also teaches Ajapa Meditation to interested individuals. To know more about him click here.

Get connected : Twitter  Facebook  Google+  LinkedIn

Posted On : 30 Nov 2010



Tags : ASP.NET jQuery JavaScript