Share

This is a continuation of the following series of posts

  1. http://railsguru.org/2010/08/introduction-to-jquery/
  2. http://railsguru.org/2010/09/getting-started-with-jquery/

Till now we have learnt how to

  • Select elements from an html page.
  • Perform operations on them
    • read/write their attributes.
    • read/write their contents.

Till now we have been performing these operations in firebug.
To actually use them, you would need to bind them to some events.

Like

  • On clicking a link show a div
  • On checking a check box, show a text box.
  • On selecting “Others” from a drop down(select box) then show a text field.

To understand the binding of functions to events, you need to first understand some JavaScript concepts.

Javascript Prereqisites

A function is just another variable

function abc()
{
}

is same as

var abc = function(){
}

Calling a function through a variable

var my_function = function(){
}

to call the function append parenthesis in the end.

my_function()

In case the function accepts any parameters, then

var my_function = function(i){}

to call the function append parenthesis in the end, and pass the parameters.

my_function(1)

Passing a function to another function

Let us consider a function named “run_ten_times” which accepts a function ‘x’ as a parameter, and calls ‘x’ 10 times.

function run_ten_times(x)
{
   for(var i = 0; i < 10; i ++ )
   {
       x();
   }

}

Calling with a variable

a) Define the function.

function f1()
{
  alert("call by variable");
}

#or

var f1 = function ()
{
  alert("call by variable");
}

b) Pass ‘x’ to the function run_ten_times()

run_ten_times(f1)

Calling with a anonymous function

Define and pass the value at the same time.

(Also see this http://helephant.com/2008/08/javascript-anonymous-functions/)

Here we will not assign the argument function to a variable, but define it directly at the place where we pass it.

run_ten_times(
function(){
  alert("from anonymous function");
}
)

Here the function which is passed as the argument is called an anonymous function as we have not assigned it a name i.e we have not assigned it to a variable.

The receiving function treats both of them(anonymous and named function) as same. It is just that the anonymous function looks more complex and difficult to understand. It is one of the concepts that makes understanding jQuery complex for beginners.

So we have seen how functions are just like other variables in JavaScript. This can also be said as functions are first class objects in Javascript.

Now we know enough javascript, which is required to understand the further sections. So lets get back to jQuery events.

Binding an event to a function

Let us take an example.

A div should hide when a link is clicked.
Consider the following html for the same.

<a id="hide_link">Hide</a>
<div id="target_div">
Click the above link to hide me.!
</div>

Create an html page with this code and open it in firefox.

We need to hide the div on clicking the link.
So we need to bind the click event on the link.
jQuery provides a function click(event_handler), for the same

click(event_handler)

This binds the function “event_handler” to the click event of all the elements in the jQuery object.
ie whenever the element(on which the click event is binded) is clicked then the function event_handler will be called.

So lets use this click function to accomplish our task.
We need to do 3 things to bind an event to an element

1) Select the element.
2) Define the handler.
3) Bing the handler on the event of the element.

For our example.
1) select the link which on click will hide the div.
2) define a function “hide_div” which hides the div.
3) bind the function “hide_div” to the click event of the selected link.

function hide_div()
{
  $("#target_div").hide();
}

$("#hide_link").click(hide_div);

With the html page you create in the above section open in firefox, write this code in firebug(do not write the code directly in html page yet!). As this is multi line code, you would need to click a small up arrow on the extreme right of the place where you type your commands in fire bug. On clicking the arrow, the input line of console opens up into a right panel. Here you can type multiline codes and execute them by clicking run at the bottom of the command window. Whenever you modify the code, you may need to refresh the page(unless you want to retain the changes) before running it, as the previous code may have made certain changes on the page.

Write the above code in the firebug, and click run. Now go and click the link and see it hiding :) .

Now Refresh the page and use the anonymous function technique to do the same.

$("#hide_link").click(function()
{
  $("#target_div").hide();
}
);

The code has become shorter, but more complex. Once you get used to it it will be fine.
Click “Run” and go ahead and click the hide link. Once again your div should hide.

A small trick.

  1. Refresh the page, and in the above code, modify the line to hide the div to following.
  2.    $("#target_div").hide(3000);

Execute the code(click ‘Run’) and then test the hide link. Do you see the div hiding slowly. Yes it has taken 3 seconds(3000 milliseconds) to hide. So the hide function optionally accepts a delay in milliseconds.
Same applies for the show() function.

Now paste the above code in the html page in the head section and reload.

<script type = text/javascript>
$("#hide_link").click(function()
{
  $("#target_div").hide();
}
);
</script>

Refresh the page and test.
The div will not hide!!

Why?
Because the binding happens before the DOM is ready/loaded. In simple terms, when the code to bind the event runs the element is not present on the page. The element comes on the page, after the code has finished running. So the event is not bound.

This can be more clear with the following analogy.
Consider event binding as tying thread to the element and the event handler.
While binding the event
1) We first select the element using selectors.
2) Tie a separate thread to each element selected and the event_handler function.

After this happens, if new elements of the same type(as already bound to the event) are added to the page, then there is no thread tied to the element, so the event does not fire for that element.

In this case, when the code executed, there were no elements on the page, so no elements were selected and no thread was tied.

For this we need to run the code once the page is loaded fully or when the DOM is ready.

jQuery provides a ready event on the document object for the same.

$(document).ready()

Following is the code to bind the ready event.

function hide_div()
{
  $("#target_div").hide();
}

function ready_handler()
{
$("#hide_link").click(hide_div);

}

$(document).ready(ready_handler);

The above code should be clear, other than the following line.

$(document).ready(ready_handler);

What this line does is binds the function ready_handler with the ready event of the document.
So the function ready_handler is called when the document is ready ie when the document has finished loading. The function ready_handler binds the function hide_div to the click event of the link #hide_link.
So when the link #hide_link is clicked the function hid_div is called which hides the div #target_div

Coming back to the line

$(document).ready(ready_handler);

Understanding document.ready

Till now we have seen that the $ accepts a selector string and returns the jQuery object/wrapped set.
But there are more ways to use the jQuery function.

We can also pass a DOM object to the $ function, and it returns the jQuery object for that object.

What is a DOM object

DOM object is the object exposed by the DOM.
Some examples of DOM objects.

1) document is the DOM object

on Firebug

>document
>$(document)

2) document.getElementById(“#hide_link”) returns an object for the link.

do this on firebug

>var a = document.getElementById("#hide_link");'
<a id="hide_link>
>var jq_a = $(a)
Object { 0=,  more...}
> a.hide()
 TypeError: a.hide is not a function { message="a.hide is not a function",  more...}
> jq_a.hide()

We first found the DOM object for the link a#hide_link by using the dom method getElementById.
Then we found the corresponding jQuery object by passing the reference to the $ function.
Now we tried calling jQuery hide() function on both the objects. We found that the hide function can only be called via the jQuery object. This not only applies to the hide() function of jQuery but to all the functions.

To summarize:
1) DOM object is the object exposed by the DOM.
2) We need to have a jQuery object in order to call the jQuery functions.
3) To get a jQuery object from a Dom object, pass the DOM object to the $ function, and the return value is the jQuery object.

Coming back to our example, let us see the same example using anonymous functions.

Anonoymous documen.ready handler

$(document).ready(function(){
 $("#hide_div").click(hide_div);
});

function hide_div()
{
   $("#target_div").hide();
}

Here instead of defining a separate function ready_handler, we pass an anonymous function to the ready event of the document.

Taking this one step further, we make the hide_div handler also anonymous.

$(document).ready(function(){
 $("#hide_div").click(function(){
    $("#target_div").hide();
});
});

The “Only document.ready function anonymous” method is more readable than making all the function anonymous. From now onwards we will follow the method of keeping the document.ready anonymous.

Show div example

Let us add another link to the above example to show the div.

Following is the html after adding the link

<a id="hide_link">Hide</a>
<a id="show_link">Show</a>
<div id="target_div">
Click the above link to hide me.!
</div>

We would need to bind a handler to the show link which will show the div.
Following would be the JavaScript code for the same.

$(document).ready(function(){
 $("#hide_div").click(hide_div);
 $("#show_div").click(show_div);
 });

function hide_div()
{
   $("#target_div").hide();
}

function show_div()
{
   $("#target_div").show();
}

The code is self explanatory.

Add a toggle link.

In the above example,we see that the hide link is only used when the div is visible and vice versa. We can make the same link do both hide and show. jQuery provides a method called toggle for the same. It does a show operation on hidden elements and hide operation on visible elements.

Following will be the code for the same

$(document).ready(function(){
 $("#toggle_div").click(toggle_div);
 });

function toggle_div()
{
   $("#target_div").toggle();
}

<a id="toggle_link">Hide/Show</a>
<div id="target_div">
Click the above link to hide me.!
</div>

Now there is only one link on click of which toggle is called.

Lets take another example

Show/hide a text box dependent on the value of dropdown.

We have a drop down to select occupation and an others option. on selecting other, the text field should be shown.

<html>
<head>
</head>
<body>
 <select id="select_fruit">
  <option value="Apple">Apple</option>
  <option value="Mango">Mango</option>
  <option value="Orange">Orange</option>
  <option value="">Others</option>
 </select>
 <input type="text" id="other_fruit"/>
 </body>
</html>

Here we need to show/hide the text box when the value of the select box changes. We can do this on the change event. jQuery provides a change() method for binding the change event.

When the value of the select box changes, we will read the value of the select box and show hide the the text field depending on the selected value.

We have seen how to read the attribute of any html tag. To read the value of a text field we could have directly used the method attr(“value”), which would have returned the text typed in the text field. You can try this out in the firebug. Similarly you could have changed the value of the textfield by doing
attr(“value”, text).
The case of a select tag is more complex. Here we cannot directly use the attr() method as the selected value is an attribute of the selected child.
jQuery provides a val() method for this.

val() returns the value of the form field ie textfield/ checkbox/ select tag/ radio button etc.
val(value) sets the value of the form field.

So to read the value of the select box you need to do

$("#select_fruit").val();

Please note that the value of the selected option is not the displayed value but the it is the value of the attribute “value” of the selected option

So if the option “Other” is selected.

  <option value="">Others</option>

We see that the value attribute of “Others” option has a blank value. So we need to test for a blank value, to test if the option selected is “Others”.

Lets go ahead and write the code.

$(document).ready(function(){

$("#select_fruit").change(show_hide_other);

})

function show_hide_other()
{
   var selected_fruit = $("#select_fruit").val();
   if(selected_fruit == "")
   {
        $("#other_fruit").show();
   }
   else
   {
         $("#other_fruit").hide();
    }
}

On the change event of the dropdown, the handler/function show_hide_other() is called.
This function will show/hide the textbox depending upon the value of the dropdown.

Go ahead and test this code. So the select box is showing and hiding as you would expect. There is only problem. On page load, the text field is visible, but the select box does not have fruits selected.

To fix this hide the text field on page load.

$(document).ready(function(){

$("#select_fruit").change(show_hide_other);
$("#other_fruit").hide();

})

This seems to work fine. But it has a problem. We are assuming that on page load the value selected in the select box will not be “Others”. This may be true for a static html page( firefox caches the value selected, so on refresh the selected value will not change, so in case of static html page also it is a problem). For a web application, the value of the drop down may be anything including “Others”, as it will come from database and it may not be the first value of the dropdown.

There are multiple ways we can fix this issue.

1) Duplicate the logic of function show_hide_other in document.ready.

$(document).ready(function(){

$("#select_fruit").change(show_hide_other);

 if($("#select_fruit").val() == "")
   {
        $("#other_fruit").show();
   }
   else
   {
         $("#other_fruit").hide();
    }

})

Note that the show_hide_other function is still there, tough have not show it above.
This is a very bad technique, as it involves code duplication, please do not follow this. I have included it here so that you know what not to do.

2) call the handler function in document.ready.

$(document).ready(function(){

$("#select_fruit").change(show_hide_other);
show_hide_other();

})

This is much better than the first option as there is no repetition of code. There is a better option though

3) Trigger the change event after binding the event.

$(document).ready(function(){
 $("#select_fruit").change(show_hide_other);
 $("#select_fruit").change();
})

jQuery provides methods to trigger the event. In general if you do not pass a parameter to the function which binds the event, it becomes a function which will trigger that event.

The above technique is very useful.
We can make a general rule for this.

Whenever the state of the page changes(certain elements are shown/hidden), then to ensure that the initial state of the page is maintained , after binding, the events which change the state, trigger the event. This will initialize the state.

Summary

We have learnt The following things in this post

We started with Javascript Basics.

  • Different ways of defining and calling a function.
  • Passing a function as a parameter to another function.
  • Anonymous Functions.
  • Functions as first class objects.

Then we moved on to events in jQuery.

  • Binding an event to a function
  • Event Handler
  • $(document).ready
  • Need of the $(document).ready function
  • Binding handlers to $(document).ready by passing named and anonymous functions.
  • The string concept of event binding.
  • Example to show/hide/toggle a div on clicking a link
  • Example to show/hide a text box depending upon the value of a dropdown.
  • The generic solution to maintain page state.

Share