Essential JavaScript and jQuery

  • 3/15/2013

Lesson 2: Working with jQuery

This lesson introduces jQuery, which is very well documented at http://jquery.com. Subsequent chapters take advantage of jQuery whenever possible to minimize typing and benefit from jQuery’s cross browser–compatible helper functions.

Introducing jQuery

jQuery is a library of helper functions that are cross browser–compatible. If you feel comfortable working with JavaScript, you might think that you don’t need jQuery, but you do. You can minimize the amount of browser-specific code you must write by using jQuery, an open-source add-in that provides an easy, browser-agnostic means for writing JavaScript.

jQuery is written in JavaScript, so it is JavaScript. You can read the jQuery source code to understand how jQuery works. Probably millions of developers use jQuery. It’s easy to use, it’s stable, it’s fully documented, and it works well with other frameworks. The following is a list of the categories of functionality jQuery provides.

  • Ajax Methods that provide synchronous and asynchronous calls to the server
  • Attributes Methods that get and set attributes of document object model (DOM) elements
  • Callbacks object An object that provides many methods for managing callbacks
  • Core Methods that provide core jQuery functionality
  • CSS Methods that get and set CSS-related properties
  • Data Methods that assist with associating arbitrary data with DOM elements
  • Deferred object A chainable object that can register multiple callbacks into callback queues and relay the success or failure state of any synchronous or asynchronous functions
  • Dimensions Helper methods for retrieving and setting DOM element dimensions
  • Effects Animation techniques that can be added to your webpage
  • Events Methods that provide the ability to register code to execute when the user interacts with the browser
  • Forms Methods that provide functionality when working with form controls
  • Offset Methods for positioning DOM elements
  • Selectors Methods that provide the ability to access DOM elements by using CSS selectors
  • Traversing Methods that provide the ability to traverse the DOM
  • Utilities Utility methods

This lesson only scratches the surface of jQuery’s capabilities, but subsequent lessons use jQuery whenever possible.

Getting started with jQuery

To get started with jQuery, add the jQuery library to your project. In this example, the QUnit testing framework has already been added to an empty web project, and it will demonstrate jQuery capabilities. You can add jQuery by either downloading the library from http://jQuery.com or adding the library from NuGet. To add it from NuGet, open your project and, in the Project menu, click Manage NuGet Packages. In the Search Online text box, type jQuery and press Enter. You should see a screen that is similar to that shown in Figure 6-9.

Figure 6-9

Figure 6-9 The NuGet package manager

After locating jQuery, click the Install button. The installation will start and, in a moment, you’ll see a green check box on jQuery, indicating that the installation has completed successfully. Click the Close button and look at the Solution Explorer window, as shown in Figure 6-10. If your project didn’t have a Scripts folder, a Scripts folder was added. Inside the Scripts folder, you’ll find the latest release of jQuery. There is a file for IntelliSense and a complete jQuery library file. Finally, there is a minimized version of jQuery, which is the file you use at production time to minimize bandwidth usage.

Figure 6-10

Figure 6-10 The completed installation of jQuery

Using jQuery

You’re probably still trying to understand what jQuery is and how you benefit from using it, so the first feature to learn is how to use jQuery to locate an element or a group of elements. First, the jQuery library must be referenced on the page on which you will be using it. In this first example, the basic QUnit Test.html file is used, and the jQuery library is added so that the file contains the following HTML.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
    <link rel="stylesheet" type="text/css" href="Content/qunit.css" />
    <script type="text/javascript" src="Scripts/qunit.js"></script>
    <script src="Scripts/jquery-1.8.2.js"></script>
    <script type="text/javascript" src="Scripts/default.js"></script>
    <script type="text/javascript" src="Scripts/tests.js"></script>
</head>
<body>
     <h1 id="qunit-header">QUnit example</h1>
     <h2 id="qunit-banner"></h2>
     <div id="qunit-testrunner-toolbar"></div>
     <h2 id="qunit-userAgent"></h2>
     <ol id="qunit-tests"></ol>
     <div id="qunit-fixture">
         test markup, will be hidden
         <input id="txtInput" type="text"  /><br />
         <input id="txtResult" type="text"  /><br />
     </div>
</body>
</html>

In the Solution Explorer window, the Test.html file has been set as the startup page by right-clicking the file and choosing Set As Start Page.

In the default.js file, the following code sets a reference to the txtInput and txtResult text boxes and then calls the clear function to initialize the two text boxes to ‘0’.

var txtInput;
var txtResult;

function initialize() {
    txtInput = document.getElementById('txtInput');
    txtResult = document.getElementById('txtResult');
    clear();
}

function clear() {
    txtInput.value = '0';
    txtResult.value = '0';
}

The tests.js file contains a simple test of the initialize method. When the test is run, the two assertions pass. The following is the tests.js file contents.

module('QUnit Test Suite', { setup: function () { initialize(); } });

test("Initialize Test", function () {
    expect(2);
    var expected = '0';
    equal(txtInput.value, expected, 'Expected value: ' + expected +
        '  Actual value: ' + txtInput.value);
    equal(txtResult.value, expected, 'Expected value: ' + expected +
        '  Actual value: ' + txtResult.value);
});

Now that the test is passing, change some code to use jQuery. The jQuery library code is in the jQuery namespace, but this namespace also has an alias of $ (dollar sign) and can be used as follows.

jQuery.someFeature
$.someFeature

You can use either of these names to access the library features, so in the interest of minimizing keystrokes, use the dollar sign. First, change the code inside the initialize function of the default.js file. The code to locate elements can be rewritten to use jQuery and CSS selectors as follows.

function initialize() {
    txtInput = $('#txtInput');
    txtResult = $('#txtResult');
    clear();
}

This code uses the CSS selector to retrieve the elements that match. In this example, there is only one match for each of the jQuery selectors. The hash (#) symbol indicates that you want to search for the id of the element. When the statement is executed, the txtInput variable will contain a jQuery object, which is a wrapper object that contains the results. This is different from the original code, in which the txtInput variable contained a direct reference to the DOM element. The wrapper object has an array of elements that match the search criteria or has no elements if there is no match. Even if the query doesn’t match any elements, txtInput still contains the wrapper object, but no elements would be in the results.

When a breakpoint is added to the code after the two statements are executed, you can debug the code and explore the jQuery wrapper, as shown in Figure 6-11.

Figure 6-11

Figure 6-11 The jQuery wrapper object for txtInput with one element

In Figure 6-11, notice there is an array element (shown as [0]), and the length property is set to 1. This is how you can verify the result of the query. Element 0 is a direct reference to the txtInput DOM element.

When you run the test, it will pass but not for the correct reason; txtInput and txtResult reference the jQuery wrapper, not the actual DOM element. When the value property is set to ‘0’, a new property is dynamically created on the jQuery object and set to ‘0’. However, the intent of this query is to set the text box value to ‘0’. To correct this problem, you can use the val method on the jQuery object. The val method gets or sets the value property of a form control that has a value property. The following is the modified test code.

module('QUnit Test Suite', { setup: function () { initialize(); } });

test("Initialize Test", function () {
    expect(2);
    var expected = '0';
    equal(txtInput.val(), expected, 'Expected value: ' + expected +
        '  Actual value: ' + txtInput.val());
    equal(txtResult.val(), expected, 'Expected value: ' + expected +
      '  Actual value: ' + txtResult.val());
});

After the four changes are made to the test, running the test shows that test assertions fail because value properties on the DOM elements are not being set. To fix the problem, modify the code in the clear function to set the value by using jQuery’s val method. The following is the completed code.

var txtInput;
var txtResult;

function initialize() {
    txtInput = $('#txtInput');
    txtResult = $('#txtResult');
    clear();
}

function clear() {
    txtInput.val('0');
    txtResult.val('0');
}

This code is complete, the tests pass, and the text boxes are populated with ‘0’. It’s important for you to use the jQuery object whenever possible so you can benefit from the cross browser–compatible features that jQuery has. If you need to reference the DOM object from the jQuery wrapper, you can do it as follows.

var domElement = $('#txtInput')[0];

Don’t forget that you can put this code inside a conditional statement that checks the length property to see whether an element exists before attempting to access element 0 of the result.

var domElement;
if($('#txtInput').length > 0){
   domElement = $('#txtInput')[0];
}

Enabling JavaScript and jQuery IntelliSense

When learning a new language or library, it’s always good to have some help to keep you from getting stuck on every statement you write. When you installed jQuery, an IntelliSense file was added, but it is not yet being used. For example, in the default.js file, if you type a jQuery expression that includes a selector and then press the Period key, you would like to see a valid list of available methods and properties. Before setting up IntelliSense, Figure 6-12 shows an example of what you see in the IntelliSense window when you type in a jQuery expression with a selector and press Period.

Figure 6-12

Figure 6-12 The IntelliSense window when not properly set up for jQuery

All the IntelliSense suggestions have a yellow warning triangle, and a message is displayed that states, “IntelliSense was unable to determine an accurate completion list for this expression. The provided list contains all identifiers in the file.”

To activate IntelliSense, you must set a reference to the jQuery file (not the IntelliSense file) in every JavaScript file that requires IntelliSense. The following is an example of the default.js file with the reference set.

/// <reference path="jquery-1.8.2.js" />
var txtInput;
var txtResult;

function initialize() {
    txtInput = $('#txtInput');
    txtResult = $('#txtResult');
    clear();
}

function clear() {
    txtInput.val('0');
    txtResult.val('0');
}

This reference was added by just dragging and dropping the jquery-1.8.2.js file to the top of the file. You can imagine that this can become a problem because you add many libraries and have hundreds of JavaScript files in a project. You might also want to benefit from IntelliSense in HTML files. To solve the problem, Microsoft has provided the ability to create a reference list and then just add the reference list to the top of the JavaScript files. You do so by adding a _references.js JavaScript file to your Scripts folder and then referencing that file in your JavaScript files. Even though you need to add the reference to the _references.js file to all your JavaScript files, when you add another library, you need to add it only to the _references.js file.

Why do you need the special name and why does it need to be in the Scripts folder when you need to reference the file explicitly? If you use a file called _references.js that is located in the Scripts folder, you automatically have a reference to this file in your HTML pages, although you still need to add the reference to your JavaScript files. The following is the contents of the _references.js file.

/// <reference path="jquery-1.8.2.js" />
/// <reference path="qunit.js" />

Visual Studio automatically locates the associated IntelliSense file, if one exists with the same name as the library, in the libraryName.intellisense.js format. In addition to using IntelliSense files if they exist, Visual Studio looks at all referenced libraries and provides default IntelliSense.

var txtResult;

function initialize() {
    txtInput = $('#txtInput');
    txtResult = $('#txtResult');
    clear();
}

function clear() {
    txtInput.val('0');
    txtResult.val('0');
}

After adding the reference, if you type a jQuery expression, you activate IntelliSense as soon as you enter the dollar sign and the opening parenthesis, as shown in Figure 6-13.

Figure 6-13

Figure 6-13 The jQuery IntelliSense providing help as you type

Notice in Figure 6-13 that after you finish typing the selector and you press Period, you are provided with a valid list of properties and methods for the jQuery wrapper object.

What happens if you are in the clear function and type txtInput and press period? Did IntelliSense make sense? You get an IntelliSense response that is similar to that in Figure 6-12. Simply put, don’t activate IntelliSense; txtInput and txtResult are global variables that can be set to anything anywhere in your application, so Visual Studio can’t possibly provide accurate IntelliSense. However, if you try typing txtInput and press Period at the bottom of the initialize function, you get proper IntelliSense that’s similar to that in Figure 6-13. The difference is that Visual Studio is examining your code and knows that you just assigned a jQuery object to txtInput, so proper IntelliSense can be provided. To take advantage of IntelliSense, the global variables are eliminated, as shown in the following, modified default.js file.

function initialize() {
    clear();
}

function clear() {
    $('#txtInput').val('0');
    $('#txtResult').val('0');
}

This code is much smaller without the global variables, but the test is now failing because the test still references the global variables. To fix the test, replace the global variable references as follows.

module('QUnit Test Suite', { setup: function () { initialize(); } });

test("Initialize Test", function () {
    expect(2);
    var expected = '0';
    equal($('#txtInput').val(), expected, 'Expected value: ' + expected +
        '  Actual value: ' + $('#txtInput').val());
    equal($('#txtResult').val(), expected, 'Expected value: ' + expected +
        '  Actual value: ' + $('#txtResult').val());
});

When the test is run, it passes.

Creating a jQuery wrapper for a DOM element reference

You’ve seen how the use of CSS selectors create a jQuery result, which is a wrapper around zero to many DOM elements that match the selector. You can also create a jQuery wrapper from a DOM element reference, as shown in the following examples.

var doc = $(document);
var innerText = $(this).text();

The first expression wraps the document object and assigns the result to a doc variable so you can use jQuery’s methods with the document object. The second expression wraps the this object, which is a DOM element being passed to an event listener. After wrapping the this object, the jQuery text method retrieves the inner text of the element and assigns it to an innerText variable.

Adding event listeners

In HTML5, you can use the addEventListener function to subscribe to an event. If you want a browser-independent way to add an event listener, you can use the jQuery .on method. There is also a corresponding .off method to remove an event listener. The .on method can be used as follows.

$('#btnSubmit').on('click', myFunction);

In this example, a button whose id is btnSubmit is located, using jQuery and the .on method to add a call to the user-defined myFunction function to the click event of the button. To remove the event listener, use the same code but replace the .on with .off as follows.

$('#btnSubmit').off('click', myFunction);

Triggering event handlers

When you need to trigger the event handlers by using code, you’ll find that jQuery can help. Probably the most common reason to trigger event handlers by using code is to test your code. Using jQuery’s trigger or the triggerHandler method causes the handler code to execute.

The trigger method causes the default behavior of the control to execute, whereas the triggerHandler method does not. For example, executing the trigger method on a submit button causes the submit action to take place in addition to executing your event handler code. Another difference is that the trigger method executes for all elements matched in the jQuery selector, whereas the triggerHandler method executes for only the first element. The following is an example of triggering the event handler code for the click event on a submit button.

$('#btnSubmit').triggerHandler('click');

Initializing code when the browser is ready

You will often need to execute initialization code after the HTML document is loaded and ready, and jQuery executes with a browser-independent way to execute code when the document is loaded as follows.

<script>
    $(document).ready(function () {
        initialize();
    });
</script>

It’s best to place this at the bottom of your HTML document and call an initialize function that contains all initialization code.

Lesson summary

  • Download jQuery from http://jQuery.com or install it from the NuGet package manager.

  • The jQuery library is in a jQuery namespace and is aliased as a dollar sign ($).

  • Use the $(selector) syntax to locate document object model (DOM) elements. The result of $(selector) is a jQuery wrapper object containing zero to many DOM elements that match the selector. You can use the length property to find out whether there are any matches to the selector.

  • Use jQuery’s val method to get or set the value of a DOM element that has a value property.

  • To enable IntelliSense, create a _references.js file in the Scripts folder and add library references to this file. In your JavaScript files, add a reference to the _references.js file.

  • Use jQuery’s .on and .off methods to add and remove event listeners.

  • Use the $(document).ready(function(){ initialize( ); }); expression to add initialization code.

Lesson review

Answer the following questions to test your knowledge of the information in this lesson. You can find the answers to these questions and explanations of why each answer choice is correct or incorrect in the “Answers” section at the end of this chapter.

  1. You want to locate all the elements on your webpage that are assigned the CSS class name Hidden. Which jQuery statement can you use?

    1. var hidden = $ (‘#Hidden’);

    2. var hidden = $ (‘.Hidden’);

    3. var hidden = $ (‘Hidden’);

    4. var hidden = $(‘class=Hidden’);

  2. You are interested in writing event-driven JavaScript code that will work on most browsers without writing browser-specific code. How can you accomplish this?

    1. Use the jQuery library to help.

    2. Use only JavaScript statements that are the same across all browsers.

    3. Do not use any JavaScript.

    4. It’s impossible to write event-driven JavaScript code that is not browser-specific.

  3. You are interested in locating all <p> elements on your webpage, so your statement is var paragraphs = $(‘p’). Which line of code would confirm whether at least one element is found?

    1. if( paragraphs.exists)

    2. if( paragraphs==null)

    3. if( paragraphs.length)

    4. if( paragraphs.count > 0)