Skip to content

Latest commit

 

History

History
1040 lines (747 loc) · 65.6 KB

ch3_jquery.asciidoc

File metadata and controls

1040 lines (747 loc) · 65.6 KB

Introducing jQuery Library

Up till now we’ve been using HTML, plain JavaScript, and CSS to create the Save The Child Web application. In the real world, developers try to be more productive by using JavaScript libraries.

There are libraries like jQuery Core that substantially minimizes the amount of manual coding while programming core functionality of a Web application. The jQuery UI library offers widgets, animations, advanced effects. The RequireJS library is a module loader that allows to modularize HTML5 applications. There are also hundreds of micro libraries that can do just one thing and can be used ala cart (visit microjs.com for details).

Libraries are different from frameworks, which we discuss in Chapter 4. While frameworks force you to organize your code a certain way, a library simply offers your components that allow you to write less code.

This chapter is about the JavaScript library jQuery or to be more precise, JQuery Core. About a half of top Web sites use jQuery (visit http://trends.builtwith.com/javascript/top for the current statistics). jQuery is simple to use and doesn’t require you to dramatically change the way you program for the Web. jQuery offers you a helping hand with the tasks that most of the Web developers need to deal with, for example, finding and manipulating DOM elements, processing browser’s events, and dealing with browser incompatibility, which makes your code more maintainable. Since jQuery is an extensible library, lots and lots of plugins were created by developers from around the world, and all of them are available for free. If you can’t find the plugin that fits your need you can create one yourself.

Note
jQuery UI library is a "close relative" of jQuery Core. It’s a set of user interface interactions, effects, widgets, and themes built on top of the jQuery. You can find in jQuery UI such widgets as Datepicker, Accordion, Slider, Tabs, Autocomplete and more. jQuery UI will also help you with adding to your Web page various interactions (e.g. drag and drop) and effects, e.g. adding CSS classes or animations (the jQuery Core also has some effects). jQuery UI is built on top of jQuery Core library and can’t be used independently. jQuery UI is covered in the O’Reilly book "jQuery UI".
Note
jQuery Mobile is yet another library built on top of jQuery Core. But this one is geared to creating mobile applications. We’ll cover it in detail in Chapter 11 of this book.

This chapter is not a detailed jQuery Core primer - there are jQuery books and the online API Documentation that provide a comprehensive explanation of jQuery. But we’ll give you just enough of the information to understand how jQuery can be used. In the section Working on Save The Child we’ll review the code of several versions of this application highlighting the benefits of using jQuery library.

Getting Started With jQuery

At the time of this writing you can download either jQuery version 1.9 or jQuery 2.0 (the latter doesn’t support Internet Explorer 6, 7, and 8). You can download one of two distributions of jQuery library. The gzipped minified version of jQuery weighs 33Kb (it’s 93Kb if unzipped), and this is all you need unless you are planning to develop jQuery plugins, in which case get a larger development version - it’s about 270Kb. We’ve downloaded jQuery from jquery.com and included it in the <script> tag in our HTML code samples so you can run them even if the Internet connection is not available.

But instead of deploying jQuery framework on your servers as a part of your application, you should use a common Content Delivery Network (CDN) URL in your HTML as shown below. Since jQuery is an extremely popular library, many Web sites use it. If more than one Web page would get it from the same CDN, the Web browser would cache it locally and reuse it rather than downloading a separate copy from different servers for every Web application that uses jQuery. The download page of jquery.com offers three CDN: Google, Microsoft, and MediaTemple. For example, if you don’t need to use HTTPS protocol with your application the MediaTemple’s CDN suffice:

<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
Tip
Using CDN may have another advantage - the content (jQuery in this case) is distributed around the globe and may be served to the user from the servers located in the same city/country thus reducing the latency.

You can provide a fallback URL by adding one extra line that will load jQuery from an alternative location if your CDN fails:

<script> window.jQuery || document.write('<script
      src="http://code.jquery.com/jquery-1.9.1.min.js"></script>')
</script>
Warning
You may find code samples that use the URL http://code.jquery.com/jquery-latest.min.js to download the latest version of jQuery. But keep in mind that by doing this you may run into a situation when some of the API of jQuery has been changed or deprecated. For example, jQuery 2.0 stopped support Internet Explorer 6, 7, and 8 and automatically switching to the latest version may result in malfunctioning of your application. We recommend using the specific version that has been tested with your application.

After covering the basics of jQuery Core, we are going to continue reviewing the code of a series of projects representing same Save The Child application, but this time using jQuery. Other than adding the validation to the Donate form and using an image slider this application remains the same in the previous chapter - we just want to show that the developers can be more productive in achieving the same result.

Some people will say that anyone who knows HTML can easily learn jQuery, but this is not so. Understanding of JavaScript is required (see Appendix A for reference). Programming with jQuery components starts with with invoking the jQuery constructor named jQuery(). But people are using the shorter version of this constructor that’s represented by a $ sign: $(). This $ property is the only object that jQuery will create in the global namespace. Everything else will be encapsulated inside the $ object.

Note
While it’s easier to write $() than jQuery(), keep in mind that if you decide to use in your application another library in addition to jQuery, with the chances are higher to run into a conflict of having another $ than another jQuery in the global name space. To make sure you won’t find yourself in the "Another day, another $" position, put your code inside a closure, passing it jQuery. The following code allows you to safely use the $ object:
(function($){
   // Your code goes here
})(jQuery);

As you remember, JavaScript functions do not require you to invoke them with exactly the same number of parameters as they were declared with. Hence, when you invoke jQuery constructor you can pass different things to it. You can pass a String as an argument or another function, and jQuery constructor will invoke different code based on the argument type. For example, if you’ll pass it a String, jQuery will assume that it’s a CSS selector, and the caller wants to find element(s) in the DOM that match this selector. Basically, you may think of it this way - whenever you want jQuery do something for you, invoke $() passing it your request.

You’ll need to get used to yet another feature of jQuery - method chaining. Each function returns an object, and you don’t have to declare a variable to hold this object. You can just write something like funcA().funcB(); This means that the method funcB() will be called on the object, returned by the funcA().

Warning
While method chaining is often presented as a great feature allowing to do more with less typing, it may complicate debugging of your code. Imagine that funcA() returned null for whatever reason. The entire chain (the funcB() in our example) that was attached to funcA() won’t be working properly, and you may need to unchain these methods to find the problem.

Also, if you need to get an access to a DOM object more than once, save the reference in a variable and reuse it rather than invoking the same selector method in several chains - it can improve the performance of your Web page.

Hello World

The "Hello World" program is always a good start to learn any new software, and we’ll go this route too. The following code sample uses jQuery to display a Web page that reads "Hello World!". Note the functions that start with the $ sign - they are all from jQuery library.

<!DOCTYPE html>
<html lang="en">
  <head>
  	<meta charset="utf-8">

  	<title>Hello jQuery</title>
  </head>
  <body>
  	<script src="js/libs/jquery-1.9.1.min.js"></script>
  	<script>
  		$(function(){                                    // (1)
  			$("body").append("<h1>Hello World!</h1>");   // (2)

  		});
  	</script>
  </body>
</html>
  1. If the script passes a function as an argument to jQuery, such a function is called when the DOM object is ready - the jQuery’s ready() function gets invoked . Keep in mind that it’s not the same as invoking a function handler window.onload, which is called after all windows resources (not just the DOM object) are completely loaded (read more in the jQuery Events section).

  2. If the script passes a String to jQuery, such String is being treated as a CSS selector, and jQuery tries to find the matching collection of HTML elements (it’ll return the reference to just one <body> in the Hello World script). This line also demonstrates the method chaining - the append() method is called on the object returned by $("body").

Selectors and Filters

Probably the most frequently used routine in a JavaScript code that’s part of the HTML page is finding DOM elements and making some manipulations with them, and this is where the jQuery’s power is. Finding HTML elements based on the CSS selectors is very easy and concise. You can specify one or more selectors in the same query. Below is a code snippet with a number of random samples of selectors. Going through this code and reading comments will help you to understand how to use jQuery selectors (note that with jQuery you can write one selector for multiple ID’s, which is not allowed in the pure JavaScript’s getElementById()).

$(".donate-button"); // find the elements with the class donate-button

$("#login-link")  // find the elements with id=login-link

// find elements with id=map-container or id=video-container
$("#map-container, #video-container");

// Find an HTML input element that has a value attribute of 200
$('input[value="200"]');

// Find all <p> elements that are nested somewhere inside <div>
$('div p');

// Find all <p> elements that are direct children (located directly inside) <div>
$('div>p');

// Find all <label> elements that are styled with the class donation-heading
$('label.donation-heading');

// Find an HTML input element that has a value attribute of 200
// and change the text of its next sibling to "two hundred"
$('input[value="200"]').next().text("two hundred");
Tip
If jQuery returns a set of elements that match the selector’s expression, you can access its elements using array notation: var theSecondDiv = $('div')[1]. If you want to iterate through the entire set use jQuery method $(selector).each(). For example, if you want to perform some function on each paragraph of an HTML document, you can do it a follows: `$("p").each(function(){…​}).

Testing jQuery Code with JSFiddle

There is a handy online site JSFiddle for performing quick testing of the code fragments of HTML, CSS, JavaScript, and popular frameworks. This Web page has a sidebar of the left and four large panels on the right. Three of these panels are for entering or copy/pasting: HTML, CSS, and JavaScript, and the forth panel is for showing the results of applying this code (see Testing jQuery using JSFiddle).

fig 05 1
Figure 1. Testing jQuery using JSFiddle

Copy/paste the fragments from the HTML and CSS written for the Donate section of the Save The Child page into the top panels, and press the button Run on JSFiddle’s toolbar, you’ll see our donate form where each radiobutton has a label in the form of digits (10, 20, 50, 100, 200). Now select jQuery 1.9.0 from the dropdown at the top left and copy paste the jQuery code fragment you’d like to test into the JavaScript panel locate under the HTML one. As you see on Testing jQuery using JSFiddle, we’ve pasted $('input[value="200"]').next().text("two hundred");. After pressing the button Run the jQuery script was executed and the label of the last radiobutton has been replaced from "200" to "two hundred" (test this fiddle here). JSFiddle’s tutorial is located at http://doc.jsfiddle.net/tutorial.html.

Tip
If you chained a method, e.g. an event handler, to the HTML element returned by a selector, your can use $(this) from inside such a handler to get a reference to this HTML element.

Filtering Elements

If jQuery selector returns a number of HTML elements, you can further narrow down this collection by applying so-called filters.jQuery has such filters as eq(), has(), first() and more.

For example, applying the selector $('label');`to the Donate section HTML fragment shown in Testing jQuery using JSFiddle would return a set of HTML elements `<label>. Say we want to change the background of the label "20" to be red. This is the third label in the HTML from Testing jQuery using JSFiddle, and the eq(n) filter selects the element at the zero-based index n within the matched set.

You can apply this filter using the following syntax: $('label:eq(2)');. But jQuery documentation suggests using the syntax $('label').eq(2); for better performance.

Using method chaining we’ll apply the filter eq(2) to the set of lables returned by the selector $('label') and then and then change the styling of the remaining HTML element(s) using the css() method that can do all CSS manipulations. This is how the entire expression will look like:

$('label').eq(2).css('background-color', 'red');

Test this script in JSFiddle or in the code of one of the Save The Child projects from this chapter. The background of the label "20" will become red. If you wanted to change the CSS of the first label in this set, the filter expressions could look as $('label:first') or, for the better performance, you should do it like this:

$('label').filter(":first").css('background-color', 'red');

If you display data in HTML table, you may want to change the background color of every even or odd row <tr>, and jQuery offers you the filters even() and odd(), for example:

$('tr').filter(":even").css('background-color', 'grey');

Usually, you’d be doing this to interactively change the background colors. You can also alternate background colors by using straight CSS selectors p:nth-child(odd) and p:nth-child(even).

Visit jQuery API documentation for the complete list of selectors and traversing filters.

Tip
If you need to display data in a grid-like form, consider using a JavaScript grid called SlickGrid.

Handling Events

Adding events processing with jQuery is simple. Your code will follow the same pattern: find the element in DOM using selector or filter, and then attach the appropriate function that handles the event. We’ll show you a handful of code samples of how to do it, but you can find the description of all methods that deal with events in the jQuery API documentation.

There are a couple of ways of passing the handler function to be executed as callback when a particular event is dispatched. For example, Our Hello World code used passes a handler function to the ready event:

$(function());

This is the same as using the following syntax:

$(document).ready(function());

For the Hello World example this was all that mattered - we just needed to have the DOM object to be able to append the <h1> element to it. But this would not be the right solution if the code needs to be executed only after all page resources have been loaded. In such case the code could have been re-written to utilize the DOM’s window.load event, which in jQuery looks as follows:

$(window).load(function(){
		$("body").append("<h1>Hello World!</h1>");
});

If the user interacts with your Web page using the mouse , the events handlers can be added using a similar procedure. For example, if you want the header in our Hello World example to process click events, find the reference to this header and attach the click() handler to it. Adding the following to the <script> section of Hello World will append the text each time the user clicks on the header.

$("h1").click(function(event){
    $("body").append("Hey, you clicked on the header!");
})

If you’d like to process double-clicks - replace the click() invocation with dblclick(). jQuery has handlers for about a dozen mouse events, which are wrapper methods to the corresponding JavaScript events that are dispatched when mouse entering or leaving the area, the mouse pointer goes up/down, or the focus moves in or out of an input field. The shorthand methods click() and dblclick() (and several others) internally use the method on(), which you can and should use in your code too (it works during the bubbling phase of the event as described in the section DOM Events in Appendix A).

Attaching Events Handlers and Elements With the Method on()

The event methods can be attached just by passing a handler function as it was done in the above examples, or to process the event or by using the on() method, which allows you to specify the native event name and the event handler as its arguments. In the section Working on Save The Child you’ll see lots of examples that use the on() method. The one liner below assigns the function handler named showLoginForm to the click event of the element with the id login-link. The following code snippets includes the commented out pure-JavaScript version of the code (see project-02-login in Chapter 1) that has the same functionality:

    // var loginLink = document.getElementById("login-link");
    // loginLink.addEventListener('click', showLoginForm, false);

	$('#login-link').on('click', showLoginForm);

The on() method allows you to assign the same handler function to more than one event. For example, to invoke the showLoginForm function when the user clicks or moves the mouse over the HTML element you could written on('click mouseover', showLoginForm).

The method off() is used for removing the event handler and the event won’t be processed anymore. For example, if you want to turn off the login link’s ability to process click event, simply write this:

	$('#login-link').off('click', showLoginForm);

Delegating Events

The method on() can be called with passing an optional selector as an argument. Since we haven’t used selectors in the example from the previous section, the event was triggered only when reached the element with an id login-link. Now imagine an HTML container that has child elements, e.g. a calculator implemented as a <div id="calculator"> containing buttons. The following code would assign a click handler to each button styled with a class .digitButton:

$("div#calculator .digitButton").on("click", function(){...});

But instead of assigning an event handler to each button, you can assign an event handler to the container and specify additional selector that child elements may be found by. The following code assigns the event handler function to only one object - the div#calculator instructing this container to invoke the event handler when any of its children matching .digitButton is clicked.

$("div#calculator").on("click", ".digitButton",function(){...});

When the button is clicked, the event bubbles up and reaches the container’s level, whose click handler will do the processing (jQuery doesn’t support capturing phase of events). The work on processing clicks for digit buttons is delegated to the container.

Another good use case for delegating event processing to a container is a financial application that displays the data in an HTML table containing hundreds of rows. Instead of assigning event hundreds event handlers (one per table row), assign one to the table. There is one extra benefit to using delegation in this case - if the application can dynamically add new rows to this table (say, the order execution data), there is no need to explicitly assign event handlers to them - the container will do the processing for both old and new rows.

Note
Starting from jQuery 1.7, the method on() is a recommended replacement of the methods bind(), unbind(), delegate(), and undelegate() that are still being used in earlier versions of jQuery. If you decide to develop your application with jQuery and its mobile version with jQuery Mobile, you need to be aware that the latter may not implement the latest code of the core jQuery. Using on() is safe though, because at the time of this writing jQuery Mobile 1.2 supports all the features of jQuery 1.8.2. In Chapter 10, you’ll see how using the responsive design principles can help you to reuse the same code on both desktop and mobile devices.

The method on() allows passing the data to the function handler (see jQuery documentation for details).

You are also allowed to assign different handlers to different events in on invocation of on(). The following code snippet from project-11-jQuery-canvas-pie-chart-json assigns handlers to focus and blur events:

$('#customAmount').on({
	focus : onCustomAmountFocus,
	blur : onCustomAmountBlur
});

AJAX with jQuery

Making AJAX requests to the server is also easier with jQuery than with pure JavaScript. All the complexity of dealing with various flavors of XMLHttpRequest is hidden from the developers. The method $.ajax() spares JavaScript developers from writing the code with multiple browser-specific ways of instantiating the XMLHttpRequest object. By invoking ajax() you can exchange the data with the server and load the JavaScript code. In its simplest form, this method takes just the URL of the remote resource to which the request is sent. Such invocation will use global defaults that should have been set in advance by invoking the method ajaxSetup().

But you can combine specifying parameters of the AJAX call and making the ajax() call. Just provide as an argument a configuration object that defines the URL, the function handlers for success and failures, and some other parameters like a function to call right before the AJAX request (beforeSend) or caching instructions for the browser (cache).

Spend some time getting familiar with all different configuration parameters that you can use with the jQuery method ajax(). Here’s a sample template for calling jQuery ajax():

$.ajax({
 		url: 'myData.json',
 		type: 'GET',
 		dataType: 'json'
	}).done(function (data) {...})
	  .fail(function (jqXHR, textStatus) {...});

This example takes a JavaScript object that defines three properties: the URL, the type of the request, and the expected data type. Using chaining, you can attach the methods done() and fail(), which have to specify the function handlers to be invoked in case of success and failure respectively. The jqXHR is a jQuery wrapper for the browser’s XMLHttpRequest object.

Don’t forget about the asynchronous nature of AJAX calls, which means that the ajax() method will be finished before the done() or fail() callbacks will be invoked. You may attach another promised callback method always() that will be invoked regardless of if the ajax() call succeeds or fails.

Note

An alternative to having a fail() handler for each ajax request is setting the global error handling routine. Consider doing this for some serious HTTP failures like 403 (access forbidden) or errors with codes 5xx. For example:

$(function() {
  $.ajaxSetup({
      error: function(jqXHR, exception) {
        if (jqXHR.status == 404) {
            alert('Requested resource not found. [404]');
        } else if (jqXHR.status == 500) {
            alert('Internal Server Error [500].');
        } else if (exception === 'parsererror') {
            alert('JSON parsing failed.');
        } else {
            alert('Got This Error:\n' + jqXHR.responseText);
        }
      }
  });
});

If you need to chain asynchronous callbacks (done(), fail(), always()) that don’t need to be called right away (they wait for the result) the method ajax() returns Deferred object. It places these callbacks in a queue to be called later. As a matter of fact, the callback fail() may never be called if no errors occurred.

If you specify JSON as a value of the dataType property, the result will be parsed automatically by jQuery - there is no need to call JSON.parse() as it was done in Chapter 2. Even though the jQuery object has a utility method parseJSON(), you don’t have to invoke it to process return of the ajax() call.

In the above example the type of the AJAX request was GET. But you can use POST too. In this case you’ll need to prepare valid JSON data to be sent to the server. In this case the configuration object that you provide as an argument to the method ajax() has to include the property data containing valid JSON.

Handy Shorthand Methods

jQuery has several shorthand methods that allow making AJAX calls with the simpler syntax, which we’ll consider next.

The method load() makes an AJAX call from an HTML element(s) to the specified URL (the first argument) and populates the HTML element with the returned data. You can pass optional second and third arguments: HTTP request parameters and the callback function to process the results. If the second argument is an object, the load() method will make a POST request, otherwise - GET. You’ll see the code that uses load() to populate states and countries from remote HTML files later in this chapter in the section on bringing the states and countries from remote HTML files. But the next line shows an example of calling load() with two parameters: the URL and the callback:

 $('#counriesList').load('data/countries.html', function(response, status, xhr){...});

The global method get() allows you to specifically issue an HTTP GET request. Similarly to the ajax() invocation, you can chain the done(), fail(), and always() methods to get(), for example:

$.get('ssc/getDonors?city=Miami', function(){alert("Got the donors");})
  .done(function(){alert("I'm called after the donors retrieved");}
  .fail(function(){alert("Request for donors failed");});
;

The global method post() makes an HTTP POST request to the server. You must specify at least one argument - the URL on the server, and, optionally, the data to be passed, the callback to be invoked on the request completion, and the type of data expected from the server. Similarly to the ajax() invocation, you can chain the done(), fail(), and always() methods to post(). The following example makes a POST request to the server passing an object with the new donor information.

$.post('ssc/addDonor', {id:123, name:"John Smith"});
;

The global method getJSON() retrieves and parses the JSON data from the specified URL and passes the JavaScript object to the specified callback. If need be, you can send the data to the server with the request. Callinf getJSON() is like calling ajax() with parameter dataType: "json".

$.getJSON('data/us-states-list.json', function (data) {
                // code to populate states combo goes here})
          .fail(function(){alert("Request for us states failed");});

The method serialize() is used when you need to submit to the server a filled out HTML <form>. This method presents the form data as a text sting in a standard URL-encoded notation. Typically, the code finds a required form using jQuery selector and then calls serialize() on this object. But you can invoke serialize() not only on the entire form, but on selected form elements too. Belows is a sample code that finds the form and serializes it.

$('form').submit(function() {
  alert($(this).serialize());
  return false;
});
Tip
Returning false from a jQuery event handler is the same as calling either preventDefault() and stopPropagation() on the jQuery.Event object. In pure JavaScript returning false doesn’t stop propagation (try to run this fiddle).

Later in this chapter in the section Submitting Donate Form you’ll see a code that uses serialize() method.

Save The Child With jQuery

In this section we’ll review code samples from several small projects (see Appendix C for running instructions) that are jQuery re-writes of the corresponding pure-JavaScript projects from Chapters 1 and 2. We are not going to add any new functionality - the goal is to demonstrate how jQuery allows you to achieve the same results while writing less code. You’ll also see how it can save you time by handling browser incompatibility for common uses (like AJAX).

Login and Donate

For example, the file main.js from project-02-jQuery-Login is 33% less in size than project-02-login written in pure JavaScript. jQuery allows your programs to be brief. For example, the next code shows how six lines of code in JavaScript can be replaced with one - the jQuery function toggle() will toggle the visibility of login-link, login-form, and login-submit.

Note. The total size of your jQuery application is not necessarily smaller comparing the pure JavaScript one because it includes the code of jQuery library.

function showLoginForm() {

// The JavaScript way
// var loginLink = document.getElementById("login-link");
// var loginForm = document.getElementById("login-form");
// var loginSubmit = document.getElementById('login-submit');
// loginLink.style.display = "none";
// loginForm.style.display = "block";
// loginSubmit.style.display = "block";


// The jQuery way
$('#login-link, #login-form, #login-submit').toggle();
}

The code of the Donation section also becomes slimmer with jQuery. For example, the following section from the JavaScript version of the application is removed:

var donateBotton = document.getElementById('donate-button');
var donationAddress = document.getElementById('donation-address');
var donateFormContainer = document.getElementById('donate-form-container');
var customAmount = document.getElementById('customAmount');
var donateForm = document.forms['_xclick'];
var donateLaterLink = document.getElementById('donate-later-link');

The jQuery method chaining allows combining (in one line) finding DOM objects and acting upon them. The following is the entire code of the main.js from project-01-jQuery-make-donation, which includes the initial version of the code of Login and Donate sections of Save The Child.

/* --------- login section -------------- */

$(function() {

  function showLoginForm() {
  	$('#login-link, #login-form, #login-submit').toggle();
  }

  $('#login-link').on('click', showLoginForm);

  function showAuthorizedSection() {
  	$('#authorized, #login-form, #login-submit').toggle();
  }

  function logIn() {
  	var userNameValue = $('#username').val();
  	var userNameValueLength = userNameValue.length;
  	var userPasswordValue = $('#password').val();
  	var userPasswordLength = userPasswordValue.length;

  	//check credentials
  	if (userNameValueLength == 0 || userPasswordLength == 0) {
  		if (userNameValueLength == 0) {
  			console.log('username is empty');
  		}
  		if (userPasswordLength == 0) {
  			console.log('password is empty');
  		}
  	} else if (userNameValue != 'admin' || userPasswordValue != '1234') {
  		console.log('username or password is invalid');
  	} else if (userNameValue == 'admin' && userPasswordValue == '1234') {
  		showAuthorizedSection();
  	}
  }

  $('#login-submit').on('click', logIn);

  function logOut() {
  	$('#username, #password').val('')
  	$('#authorized, #login-link').toggle();
  }

  $('#logout-link').on('click', logOut);

  $('#profile-link').on('click', function() {
  	console.log('Profile link was clicked');
  });
});

/* --------- make donation module start -------------- */
$(function() {
  var checkedInd = 2;  // initially checked radiobutton


  // Show/hide the donation form if the user clicks
  // on the button Donate or the Donate Later
  function showHideDonationForm() {
  	$('#donation-address, #donate-form-container').toggle();
  }
  $('#donate-button').on('click', showHideDonationForm);
  $('#donate-later-link').on('click', showHideDonationForm);
  // End of show/hide section

  $('#donate-form-container').on('click', resetOtherAmount);

  function resetOtherAmount(event) {
  	if (event.target.type == "radio") {
  		$('#otherAmount').val('');
  	}
  }

  //uncheck selected radio buttons if other amount was chosen
  function onOtherAmountFocus() {
  	var radioButtons = $('form[name="_xclick"] input:radio');
  	if ($('#otherAmount').val() == '') {
  		checkedInd = radioButtons.index(radioButtons.filter(':checked'));
  	}
  	$('form[name="_xclick"] input:radio').prop('checked', false);  // (1)
  }

  function onOtherAmountBlur() {
  	if ($('#otherAmount').val() == '') {
  		$('form[name="_xclick"] input:radio:eq(' + checkedInd + ')')
  		                     .prop("checked", true);   	 // (2)
  	}
  }
  $('#otherAmount')
      .on({focus:onOtherAmountFocus, blur:onOtherAmountBlur});  // (3)

});
  1. This one liner finds all elements of the form named _xclick, and immediately applies the jQuery filter to remove from this collection any elements except radiobuttons. Then it unchecks all of them by setting the property checked to false. This has to be done if the user places the focus inside the "Other amount" field.

  2. If the user leaves the "Other amount" return the check the previously selected radiobutton again. The eq filter picks the radiobutton whose number is equal to the value of the variable checkedInd.

  3. A single invocation of the on() method registers two event handlers: one for the focus and one for the blur event.

jQuery includes a number of effects that make the user experience more engaging. Let’s use one of them called fadeToggle(). In the code above there is a section that toggles visibility of the Donate form. If the user clicks on the Donate button, the form becomes visible (see [FIG3-11]). If the user clicks on the link "I’ll donate later", the form becomes hidden as in [FIG3-10]. The jQuery method toggle() does its job, but the change happens abruptly. The effect fadeToggle() allows to introduce slower fading which improves the user experience, at least to our taste.

If the code would hide/show just one component, the code change would be trivial - replacing toggle() with fadeToggle('slow') would do the trick. But in our case, the toggle changes visibility of two <div>'s: donation-address and donation-form-container, which should happen in a certain order. The code below is a replacement of the show/hide section in the main.js to introduce the fading effect.

function showHideDonationForm(first, next) {
        first.fadeToggle('slow', function() {
                next.fadeToggle('slow');
        });
}

var donAddress = $('#donation-address');
var donForm = $('#donate-form-container');

$('#donate-button').on('click', function() {
        showHideDonationForm(donAddress, donForm)});

$('#donate-later-link').on('click', function() {
        showHideDonationForm(donForm, donAddress)});

If you want to see the difference, first run the project-01-jQuery-make-donation and click on the Donate button (no effects), and then run project-04-jQuery-donation-ajax-json, which has the fading effect.

HTML States and Countries With jQuery AJAX

The project project-03-jQuery-donation-ajax-html illustrates retrieving the HTML data about the states and countries using jQuery method load(). Here’s the fragment from main.js that makes two load() calls. The second call purposely misspells the name of the file to generate error.

function loadData(dataUrl, target, selectionPrompt) {
  target.load(dataUrl,
              function(response, status, xhr) {               // (1)
  	if (status != "error") {
  	   target.prepend(selectionPrompt);                       // (2)
  	} else {
  	   console.log('Status: ' + status + ' ' + xhr.statusText);

  	   // Show the error message on the Web page
  	   var tempContainerHTML = '<p class="error">Error getting ' + dataUrl +
  	   ": "+ xhr.statusText + ", code: "+ xhr.status + "</p>";

       $('#temp-project-name-container').append(tempContainerHTML); // (3)
  	}
  });
}

var statePrompt =
         '<option value="" selected="selected"> - State - </option>';
loadData('data/us-states.html', $('#state'), statePrompt);

var countryPrompt =
         '<option value="" selected="selected"> - Country - </option>';

// Pass the wrong data URL on purpose
loadData('da----ta/countries.html', $('#counriesList'), countryPrompt); // (4)
  1. The callback to be invoked right after the load() completes the request.

  2. Using jQuery method prepend() insert the very first element to HTML <select> to prompt the user to select a state or a country.

  3. Display an error message at the bottom of the Web page in the <div> with ID temp-project-name-container.

  4. Pass the misspelled data URL to generate error message.

JSON States and Countries With jQuery AJAX

The project named project-04-jQuery-donation-ajax-json demonstrates how to make a jQuery ajax() call to retrieve the JSON data about countries and states and populate the respective comboboxes in the donation form. The function loadData() in the following code fragment takes three arguments: the data URL, the name of the root element in the JSON file and the target HTML element to be populated with the data retrieved from the AJAX call.

function loadData(dataUrl, rootElement, target) {
  $.ajax({
  	url: dataUrl,
  	type: 'GET',
  	cache: false,
  	timeout: 5000,                                         // (1)
  	dataType: 'json'
  }).done(function (data) {			                       // (2)
  	var optionsHTML = '';
  	$.each(data[rootElement], function(index) {
  		optionsHTML+='<option value="'+data[rootElement][index].code+'">' +
  			               data[rootElement][index].name+'</option>'
  	});

  	var targetCurrentHTML = target.html();				  //  (3)
  	var targetNewHTML = targetCurrentHTML + optionsHTML;
  	target.html(targetNewHTML);
  }).fail(function (jqXHR, textStatus, error) {            // (4)

     	console.log('AJAX request failed: ' + error +
     	                ". Code: " + jqXHR.status);

     	// The code to display the error in the
     	// browser's window goes here
  });
}

// Load the State and Country comboboxes
loadData('data/us-states-list.json',                           // (5)
                         'usstateslist', $('#state'));
loadData('data/counries-list.json',                            // (6)
                         'countrieslist', $('#counriesList'));
  1. Set the timeout. If the result of the ajax() call won’r return within 5 second, the method fail() will be invoked.

  2. The handler function to process the successfully retrieved data

  3. Get the content of the HTML <select> element to populate with states or countries. The jQuery method html() uses the browser’s innerHTML property.

  4. The handler function to process errors, if any

  5. Calling loadData() to retrieve states and populate the #state combobox. The usstatelist is the name of the root element in the json file us-states-list.json.

  6. Calling loadData() to retrieve countries and populate the #countriesList combobox

Compare this code with the pure JavaScript version from Chapter 2 that populates states and countries. If the jQuery code doesn’t seem to be shorter, keep in mind that to writing a cross-browser version in pure JavaScript would require more than a dozen of additional lines of code that deal with instantiation of XMLHttpRequest.

Run the project-04-jQuery-donation-ajax-json and open Google Developer Tools and click on the Network tab. From Calling ajax() to retrieve states and countries you can see that jQuery made two successful calls retrieving two JSON files with the data on states and countries.

fig 05 2
Figure 2. Calling ajax() to retrieve states and countries

Click on the the countries-list on the left (see The JSON with countries is successfully retrieved) and you’ll see the JSON data in the response object.

fig 05 3
Figure 3. The JSON with countries is successfully retrieved

Now let’s create an error situation to test the $.ajax().fail() chain. Just change the name of the first parameter to be data/counries.json in the loadData() invocation. There is no such file and the AJAX call will return the error 404 - see the Watch expressions in The file counries.json is not found: 404 that depicts the moment when the script execution stopped at the breakpoint in the fail() method.

fig 05 04
Figure 4. The file counries.json is not found: 404

Submitting Donate Form

Our Save The Child application should be able to submit the donation form to Paypal.com. The file index.html from project project-04-jQuery-donation-ajax-json contains the form with id="donate-form". The fragment of this form is shown below.

<form id="donate-form" name="_xclick" action="https://www.paypal.com/cgi-bin/webscr" method="post">
	<input type="hidden" name="cmd" value="_xclick">
	<input type="hidden" name="paypal_email"
	                               value="[email protected]">
	<input type="hidden" name="item_name" value="Donation">
	<input type="hidden" name="currency_code" value="USD">
	<div class="donation-form-section">
		<label class="donation-heading">Please select or enter
			<br/>
			donation amount</label>
		<input type="radio" name = "amount" id="d10" value = "10"/>
		<label for = "d10">10</label>
       ...

	</div>
	<div class="donation-form-section">
		<label class="donation-heading">Donor information</label>
		<input type="text" id="full_name" name="full_name"
		                         placeholder="full name *" required>
		<input type="email" id="email_addr" name="email_addr"
		                             placeholder="email *" required>
        ...
	</div>
	<div class="donation-form-section make-payment">
		<h4>We accept Paypal payments</h4>
		<p>
			Your payment will processed securely by <b>PayPal</b>.
		</p>
        ...
		<button class="donate-button donate-button-submit"></button>
        ...
    </div>
</form>
Manual Form Serialization

If you simply want to submit this form to the URL listed in its action property when the user clicks on the button submit, there is nothing else to be done. This already works and Paypal’s login page opens up in the browser. But if you wanted to seamlessly integrate your page with Paypal or any other third-party service, a preferred way is not to send the user to the third-party Web site, but do it without leaving your Web application. We won’t be implementing such integration with Paypal here, but technically it would be possible to pass the user’s credentials and bank information to charge the donor of Save The Child without even opening the Paypal Web page in the browser. To do this, you’d need to submit the form using AJAX and Paypal API with processing the results of this transaction using standard AJAX techniques.

To post the form to a specified URL using jQuery AJAX we’ll serialize the data from the form on submit event. The code fragment from main.js finds the form with ID donate-form and chains to it the submit() method passing to it a callback that will prepare the data and make an AJAX call. You may use the method submit() instead of attaching an event handler to process clicks on the button donate - the method submit() will be invoked not only on the Submit button click event, but when the user presses the Enter key while the cursor is in one of the form’s input fields.

$('#donate-form').submit(function() {
  var formData = $(this).serialize();
  console.log("The Donation form is serialized:" + formData);
  // Make an AJAX call here and pass the data to the server

  return false;  // stop event propagation and default action
});

Run project-04-jQuery-donation-ajax-json and open Chrome Developer Tools of Firebug. Then fill out the donation form as shown in Donation Form:

fig 05 05
Figure 5. Donation Form

Now press the Enter key and you’ll see the output in the console with the serialized form data that will look like this:

"The Donation form is serialized: cmd=_xclick&business=email-registered-in-paypal%40site-url.com&item_name=Donation&currency_code=USD&amount=50&amount=&full_name=Alex+Smith& email_addr=asmith%40gmail.com&street_address=123+Broadway&scty=New+York&zip=10013& state=NY&country=US"

Manual form serialization has other advantages too - you don’t have to pass the entire form to the server, but select only some of the input fields to be submitted. The following code snippet shows several ways of sending the partial form content.

var queryString;

queryString = $('form[name="_xclick"]')                     // (1)
               .find(':input[name=full_name],:input[name=email_addr]')
               .serialize();

queryString = $('form[name="_xclick"]')                    // (2)
               .find(':input[type=text]')
               .serialize();

queryString = $('form[name="_xclick"]')                     // (3)
                .find(':input[type=hidden]')
                .serialize();
  1. Find the form named '_xclick', apply the filter to select only the full name and the email address and serialize only these two fields.

  2. Find the form named '_xclick', apply the filter to select only the input fields of type text and serialize them

  3. Find the form named '_xclick', apply the filter to select only the hidden input fields and serialize them

We’ve prepared for you one more project illustrating manual serialization of the Donation form - project-15-jQuery-serialize-form. The main.js in this project suppresses the default processing of the form submit event and sends the form to a server side PHP script .

Note
We decided to show you a PHP example, because Java is not the only language for developing server-side code in enterprise applications. Running JavaScript on the server with Node.JS or using one of the JavaScript engines like Google’s V8 or Oracle’s Nashorn can be considered too.

For the purposes of our example, we will use a common technique of creating a server-side echo script that simply returns the data received from the server. Typically, in the enterprise IT shops the server-side development is done by a separate team, and having a dummy server will allow front-end developers lower dependency on the readiness of the server with the real data feed. The file demo.php is shown next. It’s located in the same directory where the index.html is.

<?php
if (isset($_POST['paypal_email'])) {
	$paypal_email = $_POST['paypal_email'];
	$item_name = $_POST['item_name'];
	$currency_code = $_POST['currency_code'];
	$amount = $_POST['amount'];
	$full_name = $_POST['full_name'];
	$email_addr = $_POST['email_addr'];

	echo('Got from the client and will send to PayPal: ' .
	     $paypal_email . '    Payment type: ' . $item_name .
	'   amount: ' . $amount .' '. $currency_code .
	               '    Thank you ' . $full_name
	. '    The confirmation will be sent to ' . $email_addr);

} else {
	echo('Error getting data');
}
exit();
?>

The process of integration with the payment system using Paypal API is out of this book’s scope, but at least we can identify the place to do it - it’s typically done on the server-side. In this chapter’s example it’s a server-side PHP script, but it can be a Java, .Net, Python or any other server. You’d need to replace the echo statement with the code making requests to Paypal or any other payment system. The fragment from the main.js that shows how to make a request to the demo.php comes next.

$('.donate-button-submit').on('click', submitSerializedData);

function submitSerializedData(event) {

  // disable the button to prevent more than one click
  onOffButton($('.donate-button-submit'), true, 'submitDisabled');

  event.preventDefault();                     // (1)

  var queryString;

  queryString = $('form[name="_xclick"]')    // (2)
       .find(':input[type=hidden][name!=cmd], :input[name=amount][value!=""],
       :input[name=full_name], :input[name=email_addr]')
       .serialize();

  console.log('-------- get the form inputs data  -----------');
  console.log("Submitting to the server: " + queryString);

  	$.ajax({
  	    type : 'POST',
  	    url : 'demo.php',                     // (3)
  	    data : queryString
  	}).done(function(response) {
  		console.log('-------- response from demo.php  -----------');
  		console.log("Got the response from the ajax() call to demo.php: " +
  		                                                         response);
  		// enable the donate button again
  		onOffButton($('.donate-button-submit'), false, 'submitDisabled');
  	}).fail(function (jqXHR, textStatus, error) {

        console.log('AJAX request failed: ' + error + ". Code: "
                                            + jqXHR.status);

        // The code to display the error in the
        // browser's window goes here
        });
}
  1. Prevent the default processing of the submit event - we don’t want to simply the form to the URL listed in the form’s action property.

  2. Serializing the form fields excluding the empty amounts and the hidden field with the name cmd.

  3. The serialized data from queryString will be submitted to the server-side script demo.php

Installing the XAMPP server with PHP support

The above example uses a server-side PHP script to echo data sent to it. If you’d like to see this script in action so you can test that the client and server can communicate, deployed this script in any Web server that supports PHP. For example, you can install on your computer the XAMPP package from the Apache Friends web site, which includes Apache Web Server that supports PHP, FTP, preconfigured MYSQL database server(we are not going to use it). The installation process is very simple - just go through the short instructions on the Apache Friends website that are applicable for your OS. Start the XAMPP Control application and click on the button Start next to the label Apache. By default, Apache server starts on the port 80, so entering http://localhost will open the XAMPP welcome page.

Tip
If you use MAC OS X, you may need to kill the pre-installed Apache server by using the sudo apachectl stop command.

The directory xampp/htdocs is the document root of the Apache Web Server, hence you can place the index.html of your project there or in one of its subdirectories. To test that a PHP is supported, just save the following code in the helloworld.php in the htdocs directory:

<?php
  echo('Hello World!');
?>

After entering the URL http://localhost/helloworld.php in your Web browser, you should see a greeting from this simple PHP program. The home Web page of XAMPP server contains the link phpinfo() on the left panel that shows the current configuration of your PHP server.

The easiest way to test the project-15-jQuery-serialize-form that uses demo.php is to copy this folder into the htdocs directory of your XAMPP install. Then enter the URL http://localhost/project-15-jquery-serialize-form/ in your Web browser and you’ll see the Save The Child application. Then fill out the form and click on the Donate Now button. The form will be serialized and submitted to the demo.php as explained above. If you’ll open Google Developers Tools in the Network tab you’ll see that the demo.php has received the AJAX request and the console will show the output similar to the following (for Alex Smith, [email protected]):

-------- get the form inputs data  ----------- main.js:138
Submitting to the server: paypal_email=email-registered-in-paypal%40
site-url.com&item_name=Donation+to+the+Save+Sick+Child&currency_code
=USD&amount=50&full_name=Alex+Smith&email_addr=alex%40gmail.com main.js:139

-------- response from demo.php  ----------- main.js:146
Got the response from the ajax() call to demo.php: Got from the client
and will send to PayPal: [email protected]
Payment type: Donation to the Save The Child   amount: 50 USD
Thank you Alex Smith
The confirmation will be sent to [email protected] main.js:147

jQuery Plugins

jQuery plugins are reusable components that know how to do a certain thing, for example validate a form or display images as a slide show. There are thousands of third-party jQuery plugins available in the jQuery Plugin Registry. Below are some of the useful plugins:

The chances are that you will be able to find a plugin written by someone that fits your needs. jQuery plugins are usually freely available and their source code is plain JavaScript, so you can tweak it a little if need be.

Validating the Donate Form With Plugin

The project-14-jQuery-validate illustrates the use of the jQuery validate plugin, which allows you to specify the rules to be checked when the user tries to submit the form. If the value is not valid, your custom message is displayed. We’ve included this plugin in index.html of project-14-jQuery-validate:

<script src="js/plugins/jquery.validate.min.js"></script>

To validate a form with this plugin, you need to invoke a jQuery selector finding the form and then call the method validate() on this object - this is a simplest way of using this plugin. But to have more control over the validation process you need to pass the object with validation options:

    $("#myform").validate({// validation options go here});

The file main.js includes the code to validate the Donation form. The validation options can include many options described in the plugin documentation. Our code sample uses the following options:

  • the highlight and unhighlight callbacks

  • the HTML element to be used for displaying errors

  • the name of the CSS class to style the error messages

  • the validation rules

Warning
Validating data only on the client side is not sufficient. It’s a good idea to warn the user about data issues without sending the data to the server. But ensure that the data was not corrupted/modified while traveling to the server re-validate them on the server side too. Besides, a malicious user can access your server without using your Web application. Doing server-side validation is a must.

The code fragment below displays error messages in the HTML element <div id="validationSummary"></div> that’s placed above the form in index.html. The Validator plugin provides the number of invalid form entries by invoking validator.numberOfInvalids(), and our code displays this number unless it’s equal to zero.

var validator = $('form[name="_xclick"]').validate({

  highlight : function(target, errorClass) {                    // (1)
  	$(target).addClass("invalidElement");
  	$("#validationSummary").text(validator.numberOfInvalids() +
  	                                       " field(s) are invalid");
  	$("#validationSummary").show();
  },

  unhighlight : function(target, errorClass) {                 // (2)
  	$(target).removeClass("invalidElement");

  	var errors = validator.numberOfInvalids();
  	$("#validationSummary").text( errors + " field(s) are invalid");

  	if(errors == 0) {
  		$("#validationSummary").hide();
  	}
  },

  rules : {                                                   // (3)
  	full_name : {
  		required : true,
  		minlength : 2
  	},
  	email_addr : {
  		required : true,
  		email : true
  	},
  	zip : {
  		 digits:true
  	}
  },

  	messages : {                                            // (4)
  		 full_name: {
              required: "Name is required",
            	minlength: "Name should have at least 2 letters"
          },
  		email_addr : {
  			required : "Email is required",
  		}
  	}
});
  1. When the invalid field will be highlighted, this function will be invoked. It changes the styling of the input field and updates the error count to display in the validation summary <div> on top of the form.

  2. When the error is fixed, the corrected field will be unhighlighted, and this function will be invoked. It revokes the error styling of the input field and updates the error count. If the error count is zero, the validation summary <div> becomes hidden.

  3. Set the custom validation rules for selected form fields

  4. Set the custom error messages to be displayed if the user enters invalid data.

Validator’s Error Messages shows the above code in action. After entering a one-character name and not proper email the user will see the corresponding error messages. Each message will be shown when the user leaves the corresponding field. But as soon as the user will fix any of them (e.g. enter one more letter in the name) the form will be immediately re-validated and the error messages will be removed as soon as the user fix the error.

fig 05 07
Figure 6. Validator’s Error Messages
Tip
Before including a jQuery plugin to your application spend some time testing it - check its size and compare its performance with competing plugins.

Adding Image Slider

Pretty often you need to add a rotation of the images feature to a Web page. The Save The Child page, for example, could rotate the images of the kids saved by the donors. To give you yet another illustration of using jQuery plugin, we’ve created the project called project-16-jQuery-slider, where we integrated the jQuery plugin called Responsive Carousel. The file index.html of this project includes the CSS styles and the JavaScript code plugin as follows:

<link rel="stylesheet" href="assets/css/responsive-carousel.css" />
<link rel="stylesheet" href="assets/css/responsive-carousel.slide.css" />
<link rel="stylesheet" href="assets/css/responsive-carousel.fade.css" />
<link rel="stylesheet" href="assets/css/responsive-carousel.flip.css" />
...
<script src="js/plugins/responsive-carousel/responsive-carousel.min.js"></script>
<script src="js/plugins/responsive-carousel/responsive-carousel.flip.js"></script>

Run the project-16-jQuery-slider and you’ll see how three plain slides rotate as shown on Using Responsive Carousel plugin. The HTML part of the container includes the three slides as follows.

<div id="image-carousel" class="carousel carousel-flip"
                                         data-transition="flip">
	<div>
		<img src="assets/img/slides/slide-1.jpg" />
	</div>
	<div>
		<img src="assets/img/slides/slide-2.jpg" />
	</div>
	<div>
		<img src="assets/img/slides/slide-3.jpg" />
	</div>
</div>
fig 05 08
Figure 7. Using Responsive Carousel plugin

With this plugin, the JavaScript code that the application developer has to write to implement several types of rotation is minimal. When the user clicks on the one of the radio buttons (Fade, Slide, or Flip transitions) the code below just changes the CSS class name to be used with the carousel.

$(function() {
	$("input:radio[name=transitions]").click(function() {
		var transition = $(this).val();
		var newClassName = 'carousel carousel-' + transition;
		$('#image-carousel').attr('class', '');
		$('#image-carousel').addClass(newClassName);
		$('#image-carousel').attr('data-transition', transition);
	});
});
Tip
To see code samples of using Responsive Carousel (including popular auto-playing slide shows) visit the Web page Responsive Carousel variations.

The Validator and Responsive Carousel clearly demonstrate that jQuery plugins can save you some serious time of writing code to implement some commonly required features. It’s great that the members of the jQuery community from around the world share their creations with other developers. If you can’t find a plugin that fits your needs or have specific custom logic that needs to be used or reused in your application. Should you decide to write a plugin on your own, refer to the Plugins/Authoring document.

Summary

In this chapter you became familiar with the jQuery Core library, which became the de-facto standard library in millions Web applications. Its simplicity and extensibility via the mechanism of plugins made it a must have in almost every Web page. Even if your organization standardizes decides on a more complex and feature-rich JavaScript framework, the chances are that you may find a handy jQuery plugin that will complement "the main" framework and made it into the code of your application. There is nothing wrong with this and you shouldn’t be in the position of "either jQuery or XYZ" - most likely they can coexist.

We can recommend one of the small frameworks that will complement your jQuery code is Twitter’s Bootstrap. Bootstrap can quickly make the UI of your desktop or mobile application look stylish. Bootstrap is the most popular framework on GitHub.

Chapter 7 on test-driven development will show you how to test jQuery applications. In this chapter we’ve re-written the pure JavaScript application for the illustration purposes. But if this would be a real-world project to convert the Save The Child application from JavaScript to jQuery, having tests even for the JavaScript version of the application would have helped to verify that everything transitioned to jQuery successfully.

In Chapter 11 you’ll learn how to use jQuery Mobile library - an API on top of jQuery code that allows building UI for mobile devices.

Now that we’ve covered JavaScript, HTML5 APIs, AJAX, JSON, and jQuery library, we’re going to the meat of the book: frameworks, productivity tools, and strategies for making your application enterprise-ready.