Introducing the jQuery Library - Building Your Application - Enterprise Web Development (2014)

Enterprise Web Development (2014)

Part I. Building Your Application

Chapter 3. Introducing the jQuery Library

Until 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.

Libraries such as jQuery Core substantially minimize the amount of manual coding while programming the core functionality of a web application. The jQuery UI library offers widgets, animations, and advanced effects. The RequireJS library is a module loader that allows you to modularize HTML5 applications. Hundreds of micro libraries are also available that can do just one thing and can be used à la carte (visit MicroJS for details).

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

This chapter presents the JavaScript library jQuery, or to be more precise, JQuery Core. About half of the top websites use jQuery (visit Built With 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 a helping hand with the tasks that most web developers need to deal with—for example, finding and manipulating DOM elements, processing browser events, and dealing with browser incompatibility, which makes your code more maintainable. Because jQuery is an extensible library, lots and lots of plug-ins have been created by developers from around the world, and all of them are available for free. If you can’t find the plug-in that fits your need, you can create one yourself.

NOTE

The 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 jQuery. You can find such widgets as Datepicker, Accordion, Slider, Tabs, and Autocomplete. jQuery UI will also help you add various interactions (for example, drag-and-drop) and effects (for example, adding CSS classes or animations) to your web pages. (jQuery Core also has some effects.) jQuery UI is built on top of jQuery Core and can’t be used independently. jQuery UI is covered in the jQuery UI by Eric Sarrion (O’Reilly).

NOTE

jQuery Mobile is yet another library built on top of jQuery Core. But this one is geared toward creating mobile applications. Chapter 11 covers jQuery Mobile in detail.

This chapter is not a detailed jQuery Core primer; jQuery books and the online API documentation provide a comprehensive explanation of jQuery. But we’ll give you just enough information to understand how jQuery can be used. In Programming Save The Child by Using jQuery, we’ll review the code of several versions of this application highlighting the benefits of using the 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, or 8). You can download one of two distributions of jQuery. The Gzipped minified version of jQuery is 33 KB in size (it’s 93 KB if unzipped), and this is all you need unless you are planning to develop jQuery plug-ins, in which case get the larger development version (it’s about 270 KB). 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 an Internet connection is not available.

But instead of deploying the 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 in the code that follows. Because jQuery is an extremely popular library, many websites use it. If more than one web page were to 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 CDNs: Google, Microsoft, and Media Temple. For example, if you don’t need to use HTTPS with your application, Media Temple’s CDN will suffice:

<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>

TIP

Using a CDN can have another advantage: the content (jQuery, in this case) is distributed around the globe and might be served to the user from 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 might run into a situation in which some of the API of jQuery has been changed or deprecated. For example, jQuery 2.0 stopped supporting 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 the same Save The Child application, but this time using jQuery. Other than adding validation to the Donate form and using an image slider, this application remains the same as in Chapter 2; we just want to show that developers can be more productive in achieving the same result.

Some say that anyone who knows HTML can easily learn jQuery, but this is not so. Understanding JavaScript is required (see the bonus online chapter for reference). Programming with jQuery components starts with invoking the jQuery constructor jQuery(). But people use 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

Although it’s easier to write $() than jQuery(), keep in mind that if you decide to use another library in your application in addition to jQuery, the chances are higher that you will run into a conflict with having another $ than another jQuery in the global namespace. 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 the jQuery constructor, you can pass different things to it. You can pass a string as an argument or another function, and the jQuery constructor will invoke different code based on the argument type. For example, if you 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 can 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

Although method chaining is often presented as a great feature that allows you to do more with less typing, it can complicate the debugging of your code. Imagine that funcA() returns null for whatever reason. The entire chain (funcB() in our example) attached to funcA() won’t work properly, and you might need to unchain these methods to find the problem.

Also, if you need to access 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. This can improve the performance of your web page.

Hello World

The Hello World program is always a good start when learning any new software, and we’ll go this route, too. Example 3-1 uses jQuery to display a web page that reads, “Hello World!” Note the functions that start with the $ sign—they are all from the jQuery library.

Example 3-1. Hello World with jQuery

<!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, this function is called when the DOM object is ready: the jQuery’s ready() function is invoked. Keep in mind that it’s not the same as invoking a function handler window.onload, which is called after all window resources (not just the DOM object) are completely loaded (read more in Handling Events).

2

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

Using Selectors and Filters

Probably the most frequently used routine in JavaScript code that’s part of an HTML page is finding DOM elements and manipulating them, and this is where jQuery’s power is. Finding HTML elements based on CSS selectors is easy and concise. You can specify one or more selectors in the same query. Example 3-2 presents a snippet of code that contains random samples of selectors. Going through this code and reading the comments will help you understand how to use jQuery selectors. (Note that with jQuery, you can write one selector for multiple IDs, which is not allowed in the pure JavaScript’s getElementById().)

Example 3-2. Sample jQuery selectors

$(".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 by using array notation: var theSecondDiv = $('div')[1]. If you want to iterate through the entire set, use the jQuery method $(selector).each(). For example, if you want to perform a function on each paragraph of an HTML document, you can do so as follows:$("p").each(function(){...}).

Testing jQuery Code with JSFiddle

The handy online site JSFiddle can help you perform quick testing of code fragments of HTML, CSS, JavaScript, and other popular frameworks. This web page has a sidebar on the left and four large panels on the right. Three of these panels are for entering or copying and pasting HTML, CSS, and JavaScript, respectively, and the fourth panel is for showing the results of applying this code (see Figure 3-1).

Testing jQuery by using JSFiddle

Figure 3-1. Testing jQuery by using JSFiddle

Copy and paste fragments from the HTML and CSS written for the Donate section of the Save The Child page into the top panels, and click the Run button on JSFiddle’s toolbar. You’ll see our donate form, where each radio button has a label in the form of digits (10, 20, 50, 100, 200). Now select jQuery 1.9.0 from the drop-down at the upper left and copy and paste the jQuery code fragment you’d like to test into the JavaScript panel located under the HTML one. As you see in Figure 3-1, we’ve pasted $('input[value="200"]').next().text("two hundred");. After clicking the Run button, the jQuery script executes and the label of the last radio button changes from 200 to two hundred (test this fiddle here). Also check out JSFiddle’s tutorial.

TIP

If you chained a method (for example, an event handler) to the HTML element returned by a selector, you can use $(this) from inside this handler to get a reference to this HTML element.

Filtering Elements

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

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

You can apply this filter by 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 labels returned by the selector $('label') and then change the styling of the remaining HTML element(s) by using the css() method that can perform all CSS manipulations. This is how the entire expression will look:

$('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 would look like $('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 an HTML table, you might 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 the straight CSS selectors p:nth-child(odd) and p:nth-child(even).

Check out 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 by using a selector or filter, and then attach the appropriate function that handles the event. We’ll show you a handful of examples, but you can find a 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 a callback when a particular event is dispatched. For example, our Hello World code 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 the DOM object to be able to append the <h1> element to it. But this would not be the right solution if the code needed to be executed only after all page resources have been loaded. In this case, the code could be 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 by using a mouse, the event handlers can be added by 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 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 a mouse enters or leaves 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 the bonus online chapter).

Attaching Event Handlers and Elements by Using the Method on()

Event methods can be attached just by passing a handler function, as in the preceding examples. You can also process the event by using the on() method, which allows you to specify the native event name and the event handler as its arguments. In Programming Save The Child by Using jQuery, you’ll see lots of examples that use the on() method. The following one-liner assigns the function handler named showLoginForm to the click event of the element with the id login-link. Example 3-3 includes the commented-out pure-JavaScript version of the code (seeproject-02-login in Chapter 1) that has the same functionality.

Example 3-3. Handling the click on login link

// 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 write on('click mouseover', showLoginForm).

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

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

Delegating Events

The method on() can be called by passing an optional selector as an argument. Because we haven’t used selectors in this example, the event was triggered only when it reached the element with the ID login-link. Now imagine an HTML container that has child elements—for example, a calculator implemented as <div id="calculator"> containing buttons. The following code assigns a click handler to each button styled with the 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 an additional selector that can find child elements. 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 the 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 hundreds of 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. Chapter 10 shows you how using responsive design principles can help you reuse the same code on both desktop and mobile devices.

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

You are also allowed to assign different handlers to different events in one 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

});

Using 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 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. This invocation uses global defaults that must be 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 other parameters such as a function to call right before the Ajax request (beforeSend) or caching instructions for the browser (cache).

Spend some time becoming familiar with all the 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. 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 can attach another promised callback method called always() that will be invoked regardless of whether theajax() call succeeds or fails.

NOTE

An alternative to having a fail() handler for each Ajax request is setting the global error-handling routine by using ajaxSetup(). Consider doing this for some serious HTTP failures such as 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]');

} elseif (jqXHR.status == 500) {

alert('Internal Server Error [500].');

} elseif (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 the Deferred object. It places these callbacks in a queue to be called later. As a matter of fact, the callback fail()might never be called if no errors occur.

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 was done in Chapter 2. Even though the jQuery object has a utility method called parseJSON(), you don’t have to invoke it to process a return of the ajax() call.

In the preceding example, the type of Ajax request was GET. But you can use POST, too. In this case, you need to prepare valid JSON data to be sent to the server, and 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 a 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 Loading HTML States and Countries by Using jQuery Ajax. 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. Calling getJSON() is like calling ajax() with the parameter dataType: "json", as shown in Example 3-4.

Example 3-4. Getting JSON data using an Ajax call

$.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 string in a standard URL-encoded notation. Typically, the code finds a required form by using a jQuery selector and then calls serialize()on this object. You can invoke serialize() not only on the entire form, but also on selected form elements. The following is sample code that finds the form and serializes it:

$('form').submit(function() {

alert($(this).serialize());

returnfalse;

});

TIP

Returning false from a jQuery event handler is the same as calling either preventDefault() or 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 Submitting the Donate Form, you’ll see code that uses the serialize() method.

Programming Save The Child by Using jQuery

In this section, we’ll review code samples from several small projects (see Appendix B for running instructions) that are jQuery rewrites of the corresponding pure-JavaScript projects from Chapter 1 and Chapter 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

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

NOTE

The total size of your jQuery application is not necessarily smaller than the pure JavaScript one, because it includes the code of the 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 Donate 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 you to combine (in one line) finding DOM objects and acting upon them. Example 3-5 presents the entire code of main.js from project-01-jQuery-make-donation, which includes the initial version of the code of the Login and Donate sections of Save The Child.

Example 3-5. The entire jQuery script from main.js

/* --------- 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');

}

} elseif (userNameValue != 'admin' || userPasswordValue != '1234') {

console.log('username or password is invalid');

} elseif (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

// the button Donate Now or the link I'll 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 radio buttons. Then, it deselects 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 field, return the check to the previously selected radio button again. The eq filter picks the radio button 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 preceding code, a section that toggles visibility of the Donate form. If the user clicks the Donate Now button, the form becomes visible (see Figure 1-11). If the user clicks the link “I’ll donate later,” the form becomes hidden, as in Figure 1-10. The jQuery method toggle() does its job, but the change happens abruptly. The fadeToggle() effect allows us to introduce slower fading, which improves the user experience, at least to our taste.

If you wanted to 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 following code is a replacement of the show/hide section of 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 project-01-jQuery-make-donation and click the Donate Now button (no effects), and then run project-04-jQuery-donation-ajax-json, which has the fading effect.

Loading HTML States and Countries by Using jQuery Ajax

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

Example 3-6. Loading data and processing errors

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 the jQuery method prepend(), insert the first element into the HTML element <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> section with the ID temp-project-name-container.

4

Pass the misspelled data URL to generate an error message.

Loading JSON States and Countries by Using 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 combo boxes in the donation form. The function loadData() in Example 3-7 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.

Example 3-7. Loading countries and states with ajax()

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’t return within 5 seconds, 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 combo box. 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 combo box.

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 writing a cross-browser version in pure JavaScript would require more than a dozen additional lines of code that deal with the instantiation of XMLHttpRequest.

Run project-04-jQuery-donation-ajax-json. Open Google Developer Tools and click the Network tab. In Figure 3-2, you can see that jQuery made two successful calls, retrieving two JSON files with the data on states and countries.

Calling ajax() to retrieve states and countries

Figure 3-2. Calling ajax() to retrieve states and countries

Click countries-list on the left (see Figure 3-3) and you’ll see the JSON data in the response object.

The JSON with countries is successfully retrieved

Figure 3-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 data/counries.json in the loadData() invocation. There is no such file, and the Ajax call will return the error 404. The watch expressions in Figure 3-4 depict the moment when the script execution stopped at the breakpoint in the fail() method.

The file counries.json is not found: 404

Figure 3-4. The file counries.json is not found: 404

Submitting the Donate Form

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

Example 3-8. A fragment of the Donate form

<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-registered-in-paypal@site-url.com">

<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 the Submit button, there is nothing else to be done. This already works, and PayPal’s login page opens in the browser. But if you want 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 website 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 by using Ajax, and the PayPal API would process the results of this transaction by using standard Ajax techniques.

To post the form to a specified URL by using jQuery Ajax, we’ll serialize the data from the form on the submit event. The code fragment from main.js finds the form with the 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 can use the method submit() instead of attaching an event handler to process clicks of the Donate Now button; 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

returnfalse; // stop event propagation and default action

});

Run project-04-jQuery-donation-ajax-json and open Chrome Developer Tools or Firebug. Then, fill out the donation form as shown in Figure 3-5.

The Donation form

Figure 3-5. The Donation form

Now press the Enter key, and you’ll see the output in the console with serialized form data that looks 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. For example, you don’t have to pass the entire form to the server, but select only some of the input fields to be submitted. Example 3-9 shows several ways of sending the partial form content.

Example 3-9. Samples of sending 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 file 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 such as 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 enterprise IT shops, server-side development is done by a separate team, and having a dummy server will allow frontend developers lower dependency on the readiness of the server with the real data feed. The file demo.php is shown in Example 3-10. It’s located in the same directory as index.html.

Example 3-10. The server-side script demo.php

<?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 the 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 need to replace the echo statement with the code making requests to PayPal or any other payment system. Example 3-11 is the fragment from main.js that shows how to make a request to demo.php.

Example 3-11. Submitting the Donate form to demo.php

$('.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 send the form to the URL listed in the form’s action property.

2

Serialize 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 preceding 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, deploy this script in any web server that supports PHP. For example, you can install on your computer the XAMPP package from the Apache Friends website, which includes Apache Web Server that supports PHP, FTP, and comes with a preconfigured MySQL database server (we are not going to use it). The installation process is simple: just go through the short instructions on the website that are applicable to your OS. Start the XAMPP Control application and click the Start button next to the label Apache. By default, Apache Web Server starts on port 80, so entering http://localhost opens the XAMPP welcome page.

TIP

If you use Mac OS X, you might need to kill the preinstalled Apache server by using the sudo apachectl stop command.

The directory xampp/htdocs is the document root of the Apache Web Server, so you can place the index.html of your project there or in one of its subdirectories. To test that a PHP is supported, save the following code in 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 the XAMPP server contains the link phpinfo() on the left panel that shows the current configuration of your PHP server.

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

-------- 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-registered-in-paypal@site-url.com

Payment type: Donation to the Save The Child amount: 50 USD

Thank you Alex Smith

The confirmation will be sent to alex@gmail.com main.js:147

Using jQuery Plug-ins

jQuery plug-ins are reusable components that know how to do a certain thing—for example, validate a form or display images as a slide show. Thousands of third-party jQuery plug-ins are available in the jQuery Plugin Registry. The following are some useful plug-ins:

jTable

Ajax-based tables (grids) for CRUD applications

jQuery Form

An HTML form that supports Ajax

HorizontalNav

A navigational bar with tabs that uses the full width of its container

EGrappler

A stylish Akordeon (collapsible panel)

Credit Card Validator

Detects and validates credit card numbers

Responsive Carousel

A slider to display images in a carousel fashion

morris.js

A plug-in for charting

Map Marker

Puts multiple markers on maps using Google Maps API V3

The Lazy Load plug-in

Delays loading of images, which are outside viewports

The chances are that you will be able to find a plug-in that fits your needs. jQuery plug-ins 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 by Using a Plug-in

The project-14-jQuery-validate project illustrates the use of the jQuery Validator plug-in, 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 plug-in in index.html ofproject-14-jQuery-validate:

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

To validate a form with this plug-in, you need to invoke a jQuery selector that finds the form and then call the method validate() on this object; this is the simplest way of using this plug-in. 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 routine can include many options, which are described in the plug-in 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 to ensure that the data was not corrupted/modified while traveling to the server, revalidate them on the server side too. Besides, a malicious user can access your server without using your web application. Performing server-side validation is a must.

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

Example 3-12. Displaying validation errors

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 an invalid field is highlighted, this function is 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 highlighting on the corrected field is removed, and this function is 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.

Figure 3-6 shows the preceding code in action. After entering a one-character name and an improper email address, the user will see the corresponding error messages. Each message is shown when the user leaves the corresponding field. But as soon as the user fixes any of them (for example, enters one more letter in the name), the form is immediately revalidated and the error messages are removed.

The Validator plug-in’s error messages

Figure 3-6. The Validator plug-in’s error messages

TIP

Before including a jQuery plug-in in your application, spend some time testing it. Check its size and compare its performance with competing plug-ins.

Adding an Image Slider

Often, you need to add a feature to cycle through the images on a web page. The Save The Child page, for example, could display sequential images of the kids saved by the donors. To give you yet another demonstration of using a jQuery plug-in, we’ve created the project project-16-jQuery-slider and integrated the jQuery plug-in called Responsive Carousel. The file index.html of this project includes the CSS styles and the JavaScript code plug-in, 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 project-16-jQuery-slider, and you’ll see how three plain slides display in succession, as shown in Figure 3-7. 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>

Using the Responsive Carousel plug-in

Figure 3-7. Using the Responsive Carousel plug-in

With this plug-in, the JavaScript code that the application developer has to write to implement several types of rotation is minimal. When the user clicks one of the radio buttons (Fade, Slide, or Flip Transitions) the following code 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 the Responsive Carousel plugin (including popular autoplaying slide shows), check out the Responsive Carousel variations.

The Validator and Responsive Carousel plugins clearly demonstrate that jQuery plugins can save you some serious time writing code to implement 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 plug-in that fits your needs or have specific custom logic that needs to be used or reused in your application, you can write your own plugin. Should you decide to write a plug-in of your own, refer to the Plugins/Authoring document.

Summary

In this chapter, you became familiar with the jQuery Core library, which is the de facto standard library in millions of web applications. Its simplicity and extensibility via the mechanism of plug-ins make it a must-have in almost every web page. Even if your organization decides on a more complex and feature-rich JavaScript framework, the chances are that you might find a handy jQuery plug-in that will complement “the main” framework and make 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: 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 shows you how to test jQuery applications. In this chapter, we rewrote a pure JavaScript application for illustration purposes. But if this were 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 the jQuery Mobile library—an API on top of jQuery code that allows building UIs for mobile devices.

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