Jquery Fundamentals
Jquery Fundamentals
Rebecca Murphey
http://github.com/rmurphey/jqfundamentals
With contributions by James Padolsey, Paul Irish, and others. See
the GitHub repository for a complete history of contributions.
Copyright © 2010
Licensed by Rebecca Murphey under the Creative Commons Attribution-Share Alike 3.0 United States
license. You are free to copy, distribute, transmit, and remix this work, provided you attribute the work to
Rebecca Murphey as the original author and reference the GitHub repository for the work. If you alter,
transform, or build upon this work, you may distribute the resulting work only under the same, similar or a
compatible license. Any of the above conditions can be waived if you get permission from the copyright
holder. For any reuse or distribution, you must make clear to others the license terms of this work. The
Table of Contents
1. Welcome
Getting the Code
Software
Adding JavaScript to Your Page
JavaScript Debugging
Exercises
Conventions used in this book
Reference Material
I. JavaScript 101
2. JavaScript Basics
Overview
Syntax Basics
Operators
Basic Operators
Logical Operators
Comparison Operators
Conditional Code
Truthy and Falsy Things
Switch Statements
Loops
Reserved Words
Arrays
Objects
Functions
Using Functions
Functions as Arguments
Testing Type
Scope
Closures
Selecting Elements
Saving Selections
Chaining
Dimensions
Attributes
Traversing
Manipulating Elements
Manipulating Attributes
Exercises
Selecting
Traversing
Manipulating
4. jQuery Core
$ vs $()
Utility Methods
Checking types
Data Methods
5. Events
Overview
Disconnecting Events
Namespacing Events
Event Helpers
$.fn.hover
$.fn.toggle
Exercises
6. Effects
Overview
Built-in Effects
Managing Effects
Exercises
Create a Slideshow
7. Ajax
Overview
Key Concepts
Data Types
A is for Asynchronous
$.ajax
Convenience Methods
$.fn.load
Ajax Events
Exercises
8. Plugins
What exactly is a plugin?
Writing Plugins
Adding Callbacks
Cleaning Up
Conclusion
Exercises
Make a Table Sortable
Optimize Selectors
ID-Based Selectors
Specificity
Variable Definition
Conditionals
Key Concepts
Encapsulation
Managing Dependencies
Getting RequireJS
Exercises
List of Examples
1.1. An example of inline Javascript
1.2. An example of including external JavaScript
1.3. Example of an example
2.1. A simple variable declaration
2.2. Whitespace has no meaning outside of quotation marks
2.3. Parentheses indicate precedence
2.4. Tabs enhance readability, but have no special meaning
2.5. Concatenation
2.6. Multiplication and division
2.7. Incrementing and decrementing
2.8. Addition vs. concatenation
2.9. Forcing a string to act as a number
2.10. Forcing a string to act as a number (using the unary-plus operator)
2.11. Logical AND and OR operators
2.12. Comparison operators
2.13. Flow control
2.14. Values that evaluate to true
2.15. Values that evaluate to false
2.16. The ternary operator
2.17. A switch statement
2.18. Loops
2.19. A typical for loop
2.20. A typical while loop
2.21. A while loop with a combined conditional and incrementer
2.22. A do-while loop
2.23. Stopping a loop
2.24. Skipping to the next iteration of a loop
2.25. A simple array
2.26. Accessing array items by index
2.27. Testing the size of an array
2.28. Changing the value of an array item
2.29. Adding elements to an array
2.30. Working with arrays
2.31. Creating an "object literal"
2.32. Function Declaration
2.33. Named Function Expression
2.34. A simple function
2.35. A function that returns a value
2.36. A function that returns another function
2.37. A self-executing anonymous function
2.38. Passing an anonymous function as an argument
2.39. Passing a named function as an argument
2.40. Testing the type of various variables
2.41. Functions have access to variables defined in the same scope
2.42. Code outside the scope in which a variable was defined does not have
access to the variable
2.43. Variables with the same name can exist in different scopes with
different values
2.44. Functions can "see" changes in variable values after the function is
defined
2.45. Scope insanity
2.46. How to lock in the value of i?
2.47. Locking in the value of i with a closure
3.1. A $(document).ready() block
3.2. Shorthand for $(document).ready()
3.3. Passing a named function instead of an anonymous function
3.4. Selecting elements by ID
3.5. Selecting elements by class name
3.6. Selecting elements by attribute
3.7. Selecting elements by compound CSS selector
3.8. Pseudo-selectors
3.9. Testing whether a selection contains elements
3.10. Storing selections in a variable
3.11. Refining selections
3.12. Using form-related pseduo-selectors
3.13. Chaining
3.14. Formatting chained code
3.15. Restoring your original selection using $.fn.end
3.16. The $.fn.html method used as a setter
3.17. The html method used as a getter
3.18. Getting CSS properties
3.19. Setting CSS properties
3.20. Working with classes
3.21. Basic dimensions methods
3.22. Setting attributes
3.23. Getting attributes
3.24. Moving around the DOM using traversal methods
3.25. Iterating over a selection
3.26. Changing the HTML of an element
3.27. Moving elements using different approaches
3.28. Making a copy of an element
3.29. Creating new elements
3.30. Creating a new element with an attribute object
3.31. Getting a new element on to the page
3.32. Creating and adding an element to the page at the same time
3.33. Manipulating a single attribute
3.34. Manipulating multiple attributes
3.35. Using a function to determine an attribute's new value
4.1. Checking the type of an arbitrary value
4.2. Storing and retrieving data related to an element
4.3. Storing a relationship between elements using $.fn.data
4.4. Putting jQuery into no-conflict mode
4.5. Using the $ inside a self-executing anonymous function
5.1. Event binding using a convenience method
5.2. Event biding using the $.fn.bind method
5.3. Event binding using the $.fn.bind method with data
5.4. Switching handlers using the $.fn.one method
5.5. Unbinding all click handlers on a selection
5.6. Unbinding a particular click handler
5.7. Namespacing events
5.8. Preventing a link from being followed
5.9. Triggering an event handler the right way
5.10. Event delegation using $.fn.delegate
5.11. Event delegation using $.fn.live
5.12. Unbinding delegated events
5.13. The hover helper function
5.14. The toggle helper function
6.1. A basic use of a built-in effect
6.2. Setting the duration of an effect
6.3. Augmenting jQuery.fx.speeds with custom speed definitions
6.4. Running code when an animation is complete
6.5. Run a callback even if there were no elements to animate
6.6. Custom effects with $.fn.animate
6.7. Per-property easing
7.1. Using the core $.ajax method
7.2. Using jQuery's Ajax convenience methods
7.3. Using $.fn.load to populate an element
7.4. Using $.fn.load to populate an element based on a selector
7.5. Turning form data into a query string
7.6. Creating an array of objects containing form data
7.7. Using YQL and JSONP
7.8. Setting up a loading indicator using Ajax Events
8.1. Creating a plugin to add and remove a class on hover
8.2. The Mike Alsup jQuery Plugin Development Pattern
8.3. A simple, stateful plugin using the jQuery UI widget factory
8.4. Passing options to a widget
8.5. Setting default options for a widget
8.6. Creating widget methods
8.7. Calling methods on a plugin instance
8.8. Responding when an option is set
8.9. Providing callbacks for user extension
8.10. Binding to widget events
8.11. Adding a destroy method to a widget
10.1. An object literal
10.2. Using an object literal for a jQuery feature
10.3. The module pattern
10.4. Using the module pattern for a jQuery feature
10.5. Using RequireJS: A simple example
10.6. A simple JavaScript file with dependencies
10.7. Defining a RequireJS module that has no dependencies
10.8. Defining a RequireJS module with dependencies
10.9. Defining a RequireJS module that returns a function
10.10. A RequireJS build configuration file
Chapter 1. Welcome
jQuery is fast becoming a must-have skill for front-end developers. The purpose of this book is to provide
an overview of the jQuery JavaScript library; when you're done with the book, you should be able to
complete basic tasks using jQuery, and have a solid basis from which to continue your learning. This book
was designed as material to be used in a classroom setting, but you may find it useful for individual study.
This is a hands-on class. We will spend a bit of time covering a concept, and then you’ll have the chance to
work on an exercise related to the concept. Some of the exercises may seem trivial; others may be
downright daunting. In either case, there is no grade; the goal is simply to get you comfortable working
your way through problems you’ll commonly be called upon to solve using jQuery. Example solutions to all
of the code, then uncompress it to use it on your server. If you’re git-inclined, you’re welcome to clone or
Software
You'll want to have the following tools to make the most of the class:
• For the Ajax portions: A local server (such as MAMP or WAMP), or an FTP or SSH client to
include JavaScript is important: dependencies must be included before the script that depends on them.
For the sake of page performance, JavaScript should be included as close to the end of your HTML as is
<script>
console.log('hello');
</script>
<script src='/js/jquery.js'></script>
JavaScript Debugging
A debugging tool is essential for JavaScript development. Firefox provides a debugger via the Firebug
When you are writing JavaScript code, you can use the following methods to send messages to the
console:
Other console methods are also available, though they may differ from one browser to another. The
consoles also provide the ability to set break points and watch expressions in your code for debugging
purposes.
Exercises
Most chapters in the book conclude with one or more exercises. For some exercises, you’ll be able to work
directly in Firebug; for others, you will need to include other scripts after the jQuery script tag as directed
In some cases, you will need to consult the jQuery documentation in order to complete an exercise, as we
won’t have covered all of the relevant information in the book. This is by design; the jQuery library is large,
and learning to find answers in the documentation is an important part of the process.
• First, make sure you thoroughly understand the problem you're being asked to solve.
• Next, figure out which elements you'll need to access in order to solve the problem, and
determine how you'll get those elements. Use Firebug to verify that you're getting the elements
you're after.
• Finally, figure out what you need to do with the elements to solve the problem. It can be
helpful to write comments explaining what you're going to do before you try to write the code to
do it.
Do not be afraid to make mistakes! Do not try to make your code perfect on the first try! Making mistakes
and experimenting with solutions is part of learning the library, and you’ll be a better developer for it.
Examples of solutions for these exercises are located in the /solutions directory in the sample code.
the jQuery namespace but that cannot be called on jQuery objects will be referred to as$.methodName. If
this doesn't mean much to you, don't worry — it should become clearer as you progress through the book.
Reference Material
There are any number of articles and blog posts out there that address some aspect of jQuery. Some are
phenomenal; some are downright wrong. When you read an article about jQuery, be sure it's talking about
the same version as you're using, and resist the urge to just copy and paste — take the time to understand
Here are some excellent resources to use during your jQuery learning. The most important of all is the
jQuery source itself: it contains, in code form, complete documentation of the library. It is not a black box
— your understanding of the library will grow exponentially if you spend some time visiting it now and
again — and I highly recommend bookmarking it in your browser and referring to it often.
• jQuery documentation
• jQuery forum
• Delicious bookmarks
basic concepts of JavaScript, as well as some frequent pitfalls for people who have not used JavaScript
before. While it will be of particular value to people with no programming experience, even people who
have used other programming languages may benefit from learning about some of the peculiarities of
JavaScript.
If you’re interested in learning more about the JavaScript language, I highly recommendJavaScript: The
Syntax Basics
Understanding statements, variable naming, whitespace, and other basic JavaScript syntax.
console.log('hello');
};
Operators
Basic Operators
2 * 3;
2 / 3;
var i = 1;
In JavaScript, numbers and strings will occasionally behave in ways you might not expect.
Example 2.8. Addition vs. concatenation
var foo = 1;
var foo = 1;
console.log(foo + Number(bar));
The Number constructor, when called as a function (like above) will have the effect of casting its argument
into a number. You could also use the unary plus operator, which does the same thing:
Example 2.10. Forcing a string to act as a number (using the unary-plus operator)
console.log(foo + +bar);
Logical Operators
Logical operators allow you to evaluate a series of operands using AND and OR operations.
var foo = 1;
var bar = 0;
var baz = 2;
Though it may not be clear from the example, the || operator returns the value of the first truthy operand,
or, in cases where neither operand is truthy, it'll return the last of both operands. The &&operator returns
the value of the first false operand, or the value of the last operand if both operands are truthy.
Be sure to consult the section called “Truthy and Falsy Things” for more details on which values evaluate
You'll sometimes see developers use these logical operators for flow control instead of using if statements.
For example:
// value of createBar()
beginners. I bring it up here so you'll recognize it in code you read, but I don't recommend using it until
you're extremely comfortable with what it means and how you can expect it to behave.
Comparison Operators
Comparison operators allow you to test whether values are equivalent or whether values are identical.
var foo = 1;
var bar = 0;
var bim = 2;
Conditional Code
Sometimes you only want to run a block of code under certain conditions. Flow control —
via if andelse blocks — lets you run code only under certain conditions.
console.log('hello!');
if (bar) {
} else {
if (foo) {
} else {
// this code would run if foo and bar were both false
Note
While curly braces aren't strictly required around single-line if statements, using them consistently, even
when they aren't strictly required, makes for vastly more readable code.
Be mindful not to define functions with the same name multiple times within separate if/elseblocks, as
In order to use flow control successfully, it's important to understand which kinds of values are "truthy"
and which kinds of values are "falsy." Sometimes, values that seem like they should evaluate one way
'0';
'any string';
0;
null;
Sometimes you want to set a variable to a value depending on some condition. You could use
anif/else statement, but in many cases the ternary operator is more convenient. [Definition: Theternary
operator tests a condition; if the condition is true, it returns a certain value, otherwise it returns a different
value.]
While the ternary operator can be used without assigning the return value to a variable, this is generally
discouraged.
Switch Statements
Rather than using a series of if/else if/else blocks, sometimes it can be useful to use a switch statement
instead. [Definition: Switch statements look at the value of a variable or expression, and run different
switch (foo) {
case 'bar':
break;
case 'baz':
break;
default:
break;
Switch statements have somewhat fallen out of favor in JavaScript, because often the same behavior can
be accomplished by creating an object that has more potential for reuse, testing, etc. For example:
var stuffToDo = {
'bar' : function() {
},
'baz' : function() {
},
'default' : function() {
};
if (stuffToDo[foo]) {
stuffToDo[foo]();
} else {
stuffToDo['default']();
Loops
Loops let you run a block of code a certain number of times.
Note that in Example 2.18, “Loops” even though we use the keyword var before the variable namei, this
does not "scope" the variable i to the loop block. We'll discuss scope in depth later in this chapter.
The for loop
A for loop is made up of four statements and has the following structure:
[loopBody]
The initialisation statement is executed only once, before the loop starts. It gives you an opportunity to
The conditional statement is executed before each iteration, and its return value decides whether or not
the loop is to continue. If the conditional statement evaluates to a falsey value then the loop stops.
The iteration statement is executed at the end of each iteration and gives you an opportunity to change
the state of important variables. Typically, this will involve incrementing or decrementing a counter and
The loopBody statement is what runs on every iteration. It can contain anything you want. You'll typically
have multiple statements that need to be executed and so will wrap them in a block ({...}).
evaluates to false.
var i = 0;
i++; // increment i
You'll notice that we're having to increment the counter within the loop's body. It is possible to combine
var i = -1;
}
Notice that we're starting at -1 and using the prefix incrementer (++i).
The do-while loop
This is almost exactly the same as the while loop, except for the fact that the loop's body is executed at
do {
alert('Hi there!');
} while (false);
These types of loops are quite rare since only few situations require a loop that blindly executes at least
Usually, a loop's termination will result from the conditional statement not evaluating to true, but it is
possible to stop a loop in its tracks from within the loop's body with the break statement.
break;
You may also want to continue the loop without executing more of the loop's body. This is done using
if (something) {
continue;
Reserved Words
JavaScript has a number of “reserved words,” or words that have special meaning in the language. You
should avoid using these words in your code except when using them with their intended meaning.
• break
• case
• catch
• continue
• default
• delete
• do
• else
• finally
• for
• function
• if
• in
• instanceof
• new
• return
• switch
• this
• throw
• try
• typeof
• var
• void
• while
• with
• abstract
• boolean
• byte
• char
• class
• const
• debugger
• double
• enum
• export
• extends
• final
• float
• goto
• implements
• import
• int
• interface
• long
• native
• package
• private
• protected
• public
• short
• static
• super
• synchronized
• throws
• transient
• volatile
Arrays
Arrays are zero-indexed lists of values. They are a handy way to store a set of related items of the same
type (such as strings), though in reality, an array can include multiple types of items, including other
arrays.
console.log(myArray.length); // logs 2
myArray[1] = 'changed';
While it's possible to change the value of an array item as shown in Example 2.28, “Changing the value of
myArray.push('new');
Objects
Objects contain one or more key-value pairs. The key portion can be any string. The value portion can be
any type of value: a number, a string, an array, a function, or even another object.
[Definition: When one of these values is a function, it’s called a method of the object.] Otherwise, they are
called properties.
As it turns out, nearly everything in JavaScript is an object — arrays, functions, numbers, even strings —
var myObject = {
sayHello : function() {
console.log('hello');
},
myName : 'Rebecca'
};
Note
When creating object literals, you should note that the key portion of each key-value pair can be written as
var myObject = {
validIdentifier: 123,
99999: 789
};
Object literals can be extremely useful for code organization; for more information, read Using Objects to
Functions
Functions contain blocks of code that need to be executed repeatedly. Functions can take zero or more
I prefer the named function expression method of setting a function's name, for some rather in-depth and
technical reasons. You are likely to see both methods used in others' JavaScript code.
Using Functions
console.log(text);
};
greet('Rebecca', 'Hello');
Example 2.35. A function that returns a value
return text;
};
console.log(greet('Rebecca','hello'));
};
greeting();
A common pattern in JavaScript is the self-executing anonymous function. This pattern creates a function
expression and then immediately executes the function. This pattern is extremely useful for cases where
you want to avoid polluting the global namespace with your code -- no variables declared inside of the
(function(){
})();
console.log(foo); // undefined!
Functions as Arguments
In JavaScript, functions are "first-class citizens" -- they can be assigned to variables or passed to other
console.log(result);
};
console.log(result);
};
};
Testing Type
JavaScript offers a way to test the "type" of a variable. However, the result can be confusing -- for
It's common practice to use the typeof operator when trying to determining the type of a specific value.
console.log('hello');
};
var myObject = {
foo : 'bar'
};
jQuery offers utility methods to help you determine the type of an arbitrary value. These will be covered
later.
Scope
"Scope" refers to the variables that are available to a piece of code at a given time. A lack of
When a variable is declared inside of a function using the var keyword, it is only available to code inside of
that function -- code outside of that function cannot access the variable. On the other hand, functions
defined inside that function will have access to to the declared variable.
Furthermore, variables that are declared inside a function without the var keyword are not local to the
function -- JavaScript will traverse the scope chain all the way up to the window scope to find where the
variable was previously defined. If the variable wasn't previously defined, it will be defined in the global
Example 2.41. Functions have access to variables defined in the same scope
var foo = 'hello';
console.log(foo);
};
Example 2.42. Code outside the scope in which a variable was defined does not have access to the variable
console.log(foo);
};
Example 2.43. Variables with the same name can exist in different scopes with different values
};
Example 2.44. Functions can "see" changes in variable values after the function is defined
console.log(foo);
};
foo = 'world';
return myFn;
};
var f = myFunction();
(function() {
var baz = 1;
})();
Closures
Closures are an extension of the concept of scope — functions have access to variables that were available
in the scope where the function was created. If that’s confusing, don’t worry: closures are generally best
understood by example.
In Example 2.44, “Functions can "see" changes in variable values after the function is defined” we saw
how functions have access to changing variable values. The same sort of behavior exists with functions
defined within loops -- the function "sees" the change in the variable's value even after the function is
$('<p>click me</p>').appendTo('body').click(function() {
alert(i);
});
};
$('<p>click me</p>').appendTo('body').click(createFunction(i));
for you; code included inside $(document).ready() will only run once the page is ready for JavaScript code
to execute.
$(document).ready(function() {
console.log('ready!');
});
There is a shorthand for $(document).ready() that you will sometimes see; however, I recommend against
using it if you are writing code that people who aren't experienced with jQuery may see.
$(function() {
console.log('ready!');
});
You can also pass a named function to $(document).ready() instead of passing an anonymous function.
function readyFn() {
$(document).ready(readyFn);
Selecting Elements
The most basic concept of jQuery is to “select some elements and do something with them.” jQuery
supports most CSS3 selectors, as well as some non-standard selectors. For a complete selector reference,
visit http://api.jquery.com/category/selectors/.
$('a.external:first');
$('tr:odd');
$('div:visible');
$('div:gt(2)'); // all except the first three divs
Note
When you use the :visible and :hidden pseudo-selectors, jQuery tests the actual visibility of the element,
not its CSS visibility or display — that is, it looks to see if the element's physical height and width on the
page are both greater than zero. However, this test doesn't work with <tr> elements; in this case, jQuery
does check the CSS display property, and considers an element hidden if its display property is set
to none. Elements that have not been added to the DOM will always be considered hidden, even if the CSS
that would affect them would render them visible. (See the Manipulation section later in this chapter to
For reference, here is the code jQuery uses to determine whether an element is visible or hidden, with
true :
false :
};
};
Choosing Selectors
Choosing good selectors is one way to improve the performance of your JavaScript. A little specificity — for example, including an
element type such as div when selecting elements by class name — can go a long way. Generally, any time you can give jQuery a
hint about where it might expect to find what you're looking for, you should. On the other hand, too much specificity can be a bad
thing. A selector such as #myTable thead tr th.special is overkill if a selector such as #myTable th.special will get you what you
want.
jQuery offers many attribute-based selectors, allowing you to make selections based on the content of arbitrary attributes using
$("a[rel$='thinger']");
While these can be useful in a pinch, they can also be extremely slow — I once wrote an attribute-based selector that locked up my
page for multiple seconds. Wherever possible, make your selections using IDs, class names, and tag names.
Want to know more? Paul Irish has a great presentation about improving performance in JavaScript, with several slides focused
Once you've made a selection, you'll often want to know whether you have anything to work with. You may
if ($('div.foo')) { ... }
This won't work. When you make a selection using $(), an object is always returned, and objects always
evaluate to true. Even if your selection doesn't contain any elements, the code inside the ifstatement will
still run.
Instead, you need to test the selection's length property, which tells you how many elements were
selected. If the answer is 0, the length property will evaluate to false when used as a boolean value.
if ($('div.foo').length) { ... }
Saving Selections
Every time you make a selection, a lot of code runs, and jQuery doesn't do caching of selections for you. If
you've made a selection that you might need to make again, you should save the selection in a variable
Note
In Example 3.10, “Storing selections in a variable”, the variable name begins with a dollar sign. Unlike in
other languages, there's nothing special about the dollar sign in JavaScript -- it's just another character. We
use it here to indicate that the variable contains a jQuery object. This practice -- a sort of Hungarian
Once you've stored your selection, you can call jQuery methods on the variable you stored it in just like
A selection only fetches the elements that are on the page when you make the selection. If you add
elements to the page later, you'll have to repeat the selection or otherwise add them to the selection
stored in the variable. Stored selections don't magically update when the DOM changes.
Sometimes you have a selection that contains more than what you're after; in this case, you may want to
refine your selection. jQuery offers several methods for zeroing in on exactly what you're after.
jQuery offers several pseudo-selectors that help you find elements in your forms; these are especially
helpful because it can be difficult to distinguish between form elements based on their state or type using
:button
:checkbox
:checked
:disabled
:file
:image
:input
:password
:radio
:reset
:selected
:submit
:text
flavors: getters and setters. Getters return a property of the first selected element; setters set a property
Chaining
If you call a method on a selection and that method returns a jQuery object, you can continue to call
If you are writing a chain that includes several steps, you (and the person who comes after you) may find
your code more readable if you break the chain over several lines.
$('#content')
.find('h3')
.eq(2)
If you change your selection in the midst of a chain, jQuery provides the $.fn.end method to get you back
$('#content')
.find('h3')
.eq(2)
.eq(0)
Chaining is extraordinarily powerful, and it's a feature that many libraries have adapted since it was made
popular by jQuery. However, it must be used with care. Extensive chaining can make code extremely
difficult to modify or debug. There is no hard-and-fast rule to how long a chain should be -- just know that it
jQuery “overloads” its methods, so the method used to set a value generally has the same name as the
method used to get a value. When a method is used to set a value, it is called a setter. When a method is
used to get (or read) a value, it is called a getter. Setters affect all elements in a selection; getters get the
$('h1').html('hello world');
$('h1').html();
Setters return a jQuery object, allowing you to continue to call jQuery methods on your selection; getters
return whatever they were asked to get, meaning you cannot continue to call jQuery methods on the value
Note
CSS properties that normally include a hyphen need to be camel cased in JavaScript. For example, the CSS
Note the style of the argument we use on the second line -- it is an object that contains multiple
properties. This is a common way to pass multiple arguments to a function, and many jQuery setter
As a getter, the $.fn.css method is valuable; however, it should generally be avoided as a setter in
production-ready code, because you don't want presentational information in your JavaScript. Instead,
write CSS rules for classes that describe the various visual states, and then simply change the class on the
$h1.addClass('big');
$h1.removeClass('big');
$h1.toggleClass('big');
if ($h1.hasClass('big')) { ... }
Classes can also be useful for storing state information about an element, such as indicating that an
element is selected.
Dimensions
jQuery offers a variety of methods for obtaining and modifying dimension and position information about
an element.
The code in Example 3.21, “Basic dimensions methods” is just a very brief overview of the dimensions
visithttp://api.jquery.com/category/dimensions/.
Attributes
An element's attributes can contain useful information for your application, so it's important to be able to
The $.fn.attr method acts as both a getter and a setter. As with the $.fn.css method,$.fn.attr as a setter
can accept either a key and a value, or an object containing one or more key/value pairs.
Example 3.22. Setting attributes
$('a').attr('href', 'allMyHrefsAreTheSameNow.html');
$('a').attr({
'href' : 'somethingNew.html'
});
This time, we broke the object up into multiple lines. Remember, whitespace doesn't matter in JavaScript,
so you should feel free to use it liberally to make your code more legible! You can use a minification tool
$('a').attr('href'); // returns the href for the first a element in the document
Traversing
Once you have a jQuery selection, you can find other elements using your selection as a starting point.
Be cautious with traversing long distances in your documents -- complex traversal makes it imperative that
your document's structure remain the same, something that's difficult to guarantee even if you're the one
creating the whole application from server to client. One- or two-step traversal is fine, but you generally
want to avoid traversals that take you from one container to another.
$('h1').next('p');
$('div:visible').parent();
$('input[name=first_name]').closest('form');
$('#myList').children();
$('li.selected').siblings();
You can also iterate over a selection using $.fn.each. This method iterates over all of the elements in a
selection, and runs a function for each one. The function receives the index of the current element and the
DOM element itself as arguments. Inside the function, the DOM element is also available as this by default.
console.log(
$(el).html()
);
});
Manipulating Elements
Once you've made a selection, the fun begins. You can change, move, remove, and clone elements. You
visithttp://api.jquery.com/category/manipulation/.
Getting and Setting Information about Elements
There are any number of ways you can change an existing element. Among the most common tasks you'll
perform is changing the inner HTML or attribute of an element. jQuery offers simple, cross-browser
methods for these sorts of manipulations. You can also get information about elements using many of the
same methods in their getter incarnations. We'll see examples of these throughout this section, but
specifically, here are a few methods you can use to get and set information about elements.
Note
Changing things about elements is trivial, but remember that the change will affect all elements in the
selection, so if you just want to change one element, be sure to specify that in your selection before calling
a setter method.
Note
When methods act as getters, they generally only work on the first element in the selection, and they do
not return a jQuery object, so you can't chain additional methods to them. One notable exception
is $.fn.text; as mentioned below, it gets the text for all elements in the selection.
$.fn.html
$.fn.text
$.fn.attr
$.fn.width
Get or set the width in pixels of the first element in the selection as an integer.
$.fn.height
Get or set the height in pixels of the first element in the selection as an integer.
$.fn.position
Get an object with position information for the first element in the selection, relative to its first
$.fn.val
$('#myDiv p:first')
.html('New <strong>first</strong> paragraph!');
There are a variety of ways to move elements around the DOM; generally, there are two approaches:
For example, jQuery provides $.fn.insertAfter and $.fn.after. The $.fn.insertAftermethod places the
selected element(s) after the element that you provide as an argument; the$.fn.after method places the
element provided as an argument after the selected element. Several other methods follow this
The method that makes the most sense for you will depend on what elements you already have selected,
and whether you will need to store a reference to the elements you're adding to the page. If you need to
store a reference, you will always want to take the first approach -- placing the selected elements relative
$('#myList').append($('#myList li:first'));
When you use methods such as $.fn.appendTo, you are moving the element; sometimes you want to make
a copy of the element instead. In this case, you'll need to use $.fn.clone first.
$('#myList li:first').clone().appendTo('#myList');
Note
If you need to copy related data and events, be sure to pass true as an argument to $.fn.clone.
Removing Elements
There are two ways to remove elements from the page: $.fn.remove and $.fn.detach. You'll
use $.fn.remove when you want to permanently remove the selection from the page; while the method
does return the removed element(s), those elements will not have their associated data and events
If you need the data and events to persist, you'll want to use $.fn.detach instead. Like$.fn.remove, it
returns the selection, but it also maintains the data and events associated with the selection, so you can
The $.fn.detach method is extremely valuable if you are doing heavy manipulation to an element. In that
case, it's beneficial to $.fn.detach the element from the page, work on it in your code, and then restore it
to the page when you're done. This saves you from expensive "DOM touches" while maintaining the
If you want to leave the element on the page but simply want to remove its contents, you can
jQuery offers a trivial and elegant way to create new elements using the same $() method you use to
make selections.
$('<a/>', {
'class' : 'new',
href : 'foo.html'
});
Note that in the attributes object we included as the second argument, the property name class is quoted,
while the property names text and href are not. Property names generally do not need to be quoted unless
When you create a new element, it is not immediately added to the page. There are several ways to add
$myNewElement.appendTo('#content');
method to add the element to the page directly after the $(). However, most of the time you will want a
reference to the element you added, so you don't need to select it later.
You can even create an element as you're adding it to the page, but note that in this case you don't get a
Example 3.32. Creating and adding an element to the page at the same time
$('ul').append('<li>list item</li>');
Note
The syntax for adding new elements to the page is so easy, it's tempting to forget that there's a huge
performance cost for adding to the DOM repeatedly. If you are adding many elements to the same
container, you'll want to concatenate all the html into a single string, and then append that string to the
container instead of appending the elements one at a time. You can use an array to gather all the pieces
$myList.append(myItems.join(''));
Manipulating Attributes
jQuery's attribute manipulation capabilities are extensive. Basic changes are simple, but the $.fn.attr
$('#myDiv a:first').attr({
href : 'newDestination.html',
rel : 'super-special'
});
$('#myDiv a:first').attr({
rel : 'super-special',
href : function() {
});
});
Exercises
Selecting
Open the file /exercises/index.html in your browser. Use the file/exercises/js/sandbox.js or work in Firebug
3. Select the label for the search input using an attribute selector.
4. Figure out how many elements on the page are hidden (hint: .length).
5. Figure out how many image elements on the page have an alt attribute.
Traversing
Open the file /exercises/index.html in your browser. Use the file/exercises/js/sandbox.js or work in Firebug
1. Select all of the image elements on the page; log each image's alt attribute.
2. Select the search input text box, then traverse up to the form and add a class to the form.
3. Select the list item inside #myList that has a class of "current" and remove that class from it;
4. Select the select element inside #specials; traverse your way to the submit button.
5. Select the first list item in the #slideshow element; add the class "current" to it, and then add
Manipulating
Open the file /exercises/index.html in your browser. Use the file/exercises/js/sandbox.js or work in Firebug
1. Add five new list items to the end of the unordered list #myList. Hint:
4. Add another option to the select element; give the option the value "Wednesday"
5. Add a new div.module to the page after the last one; put a copy of one of the existing images
inside of it.
$('h1').remove();
Most jQuery methods are called on jQuery objects as shown above; these methods are said to be part of
the $.fn namespace, or the “jQuery prototype,” and are best thought of as jQuery object methods.
However, there are several methods that do not act on a selection; these methods are said to be part of
the jQuery namespace, and are best thought of as core jQuery methods.
This distinction can be incredibly confusing to new jQuery users. Here’s what you need to remember:
• Methods called on jQuery selections are in the $.fn namespace, and automatically receive
• Methods in the $ namespace are generally utility-type methods, and do not work with
selections; they are not automatically passed any arguments, and their return value will vary.
There are a few cases where object methods and core methods have the same names, such
as$.each and $.fn.each. In these cases, be extremely careful when reading the documentation that you
Utility Methods
jQuery offers several utility methods in the $ namespace. These methods are helpful for accomplishing
routine programming tasks. Below are examples of a few of the utility methods; for a complete reference
$.trim
$.each
});
});
Note
There is also a method $.fn.each, which is used for iterating over a selection of elements.
$.inArray
var myArray = [ 1, 2, 3, 5 ];
console.log('found it!');
$.extend
Changes the properties of the first object using the properties of subsequent objects.
console.log(firstObject.foo); // 'baz'
console.log(newObject.foo); // 'baz'
If you don't want to change any of the objects you pass to $.extend, pass an empty object as the
first argument.
console.log(firstObject.foo); // 'bar'
console.log(newObject.foo); // 'baz'
$.proxy
Returns a function that will always run in the provided scope — that is, sets the meaning
If you have an object with methods, you can pass the object and the name of a method to return
var myObject = {
myFn : function() {
console.log(this);
};
Checking types
As mentioned in the "JavaScript basics" section, jQuery offers a few basic utility methods for determining
jQuery.isFunction(myValue); // false
jQuery.isPlainObject(myValue); // false
jQuery.isArray(myValue); // true
Data Methods
As your work with jQuery progresses, you'll find that there's often data about an element that you want to
store with the element. In plain JavaScript, you might do this by adding a property to the DOM element,
but you'd have to deal with memory leaks in some browsers. jQuery offers a straightforward way to store
data related to an element, and it manages the memory issues for you.
You can store any kind of data on an element, and it's hard to overstate the importance of this when you
get into complex application development. For the purposes of this class, we'll mostly use$.fn.data to store
For example, we may want to establish a relationship between a list item and a div that's inside of it. We
could establish this relationship every single time we interact with the list item, but a better solution would
be to establish the relationship once, and then store a pointer to the div on the list item using $.fn.data:
$('#myList li').each(function() {
$li.data('contentDiv', $div);
});
$firstLi.data('contentDiv').html('new content');
In addition to passing $.fn.data a single key-value pair to store data, you can also pass an object
jQuery offers the $.support object, as well as the deprecated $.browser object, for this purpose. For
The $.support object is dedicated to determining what features a browser supports; it is recommended as
a more “future-proof” method of customizing your JavaScript for different browser environments.
The $.browser object was deprecated in favor of the $.support object, but it will not be removed from
jQuery anytime soon. It provides direct detection of the browser brand and version.
In order to avoid these conflicts, you need to put jQuery in no-conflict mode immediately after it is loaded
onto the page and before you attempt to use jQuery in your page.
When you put jQuery into no-conflict mode, you have the option of assigning a variable name to replace $.
<script src="prototype.js"></script>
<script src="jquery.js"></script>
<script>var $j = jQuery.noConflict();</script>
You can continue to use the standard $ by wrapping your code in a self-executing anonymous function;
this is a standard pattern for plugin authoring, where the author cannot know whether another library will
<script src="prototype.js"></script>
<script src="jquery.js"></script>
<script>
jQuery.noConflict();
(function($) {
})(jQuery);
</script>
Chapter 5. Events
Overview
jQuery provides simple methods for attaching event handlers to selections. When an event occurs, the
provided function is executed. Inside the function, this refers to the element that was clicked.
The event handling function can receive an event object. This object can be used to determine the nature
most often. These methods -- including $.fn.click, $.fn.focus, $.fn.blur,$.fn.change, etc. -- are shorthand for
jQuery's $.fn.bind method. The bind method is useful for binding the same hadler function to multiple
events, and is also used when you want to provide data to the event hander, or when you are working with
custom events.
$('p').click(function() {
console.log('click');
});
$('p').bind('click', function() {
console.log('click');
});
Example 5.3. Event binding using the $.fn.bind method with data
$('input').bind(
function(eventObject) {
console.log(eventObject.type, eventObject.data);
);
Connecting Events to Run Only Once
Sometimes you need a particular handler to run only once -- after that, you may want no handler to run, or
you may want a different handler to run. jQuery provides the $.fn.one method for this purpose.
$('p').one('click', function() {
});
The $.fn.one method is especially useful if you need to do some complicated setup the first time an
To disconnect an event handler, you use the $.fn.unbind method and pass in the event type to unbind. If
you attached a named function to the event, then you can isolate the unbinding to that named function by
$('p').unbind('click');
Namespacing Events
For complex applications and for plugins you share with others, it can be useful to namespace your events
so you don't unintentionally disconnect events that you didn't or couldn't know about.
$('p').unbind('click.myNamespace');
properties and methods. The event object is most commonly used to prevent the default action of the
event via the preventDefault method. However, the event object contains a number of other useful
pageX, pageY
The mouse position at the time the event occurred, relative to the top left of the page.
type
which
data
Any data that was passed in when the event was bound.
target
preventDefault()
In addition to the event object, the event handling function also has access to the DOM element that the
handler was bound to via the keyword this. To turn the DOM element into a jQuery object that we can use
$('a').click(function(e) {
if ($this.attr('href').match('evil')) {
e.preventDefault();
$this.addClass('evil');
});
the $.fn.trigger method. While this method has its uses, it should not be used simply to call a function that
was bound as a click handler. Instead, you should store the function you want to call in a variable, and
pass the variable name when you do your binding. Then, you can call the function itself whenever you
if (e) {
console.log(e);
} else {
};
$('p').click(foo);
events to those new elements -- events you already bound to similar elements that were on the page
originally. Instead of repeating your event binding every time you add elements to the page, you can use
event delegation. With event delegation, you bind your event to a container element, and then when the
event occurs, you look to see which contained element it occurred on. If this sounds complicated, luckily
While most people discover event delegation while dealing with elements added to the page later, it has
some performance benefits even if you never add more elements to the page. The time required to bind
event handlers to hundreds of individual elements is non-trivial; if you have a large set of elements, you
Note
The $.fn.live method was introduced in jQuery 1.3, and at that time only certain event types were
supported. As of jQuery 1.4.2, the $.fn.delegatemethod is available, and is the preferred method.
});
// ...
});
If you need to remove delegated events, you can't simply unbind them. Instead, use$.fn.undelegate for
events connected with $.fn.delegate, and $.fn.die for events connected with $.fn.live. As with bind, you
$('#myUnorderedList').undelegate('li', 'click');
$('#myUnorderedList li').die('click');
Event Helpers
jQuery offers two event-related helper functions that save you a few keystrokes.
$.fn.hover
The $.fn.hover method lets you pass one or two functions to be run when
the mouseenter andmouseleave events occur on an element. If you pass one function, it will be run for
both events; if you pass two functions, the first will run for mouseenter, and the second will run
for mouseleave.
Note
$('#menu li').hover(function() {
$(this).toggleClass('hover');
});
$.fn.toggle
Much like $.fn.hover, the $.fn.toggle method receives two or more functions; each time the event occurs,
the next function in the list is called. Generally, $.fn.toggle is used with just two functions, but technically
$('p.expander').toggle(
function() {
$(this).prev().addClass('open');
},
function() {
$(this).prev().removeClass('open');
);
Exercises
Create an Input Hint
Open the file /exercises/index.html in your browser. Use the file/exercises/js/inputHint.js or work in Firebug.
Your task is to use the text of the label for the search input to create "hint" text for the search input. The
1. Set the value of the search input to the text of the label element
2. Add a class of "hint" to the search input
4. Bind a focus event to the search input that removes the hint text and the "hint" class
5. Bind a blur event to the search input that restores the hint text and "hint" class if no search
What other considerations might there be if you were creating this functionality for a real site?
Open the file /exercises/index.html in your browser. Use the file/exercises/js/tabs.js. Your task is to create
3. Iterate over the modules using $.fn.each. For each module, use the text of the h2 element as
the text for a list item that you add to the unordered list element.
Chapter 6. Effects
Overview
jQuery makes it trivial to add simple effects to your page. Effects can use the built-in settings, or provide a
customized duration. You can also create custom animations of arbitrary CSS properties.
Built-in Effects
Frequently used effects are built into jQuery as methods:
$.fn.show
$.fn.hide
$.fn.fadeIn
$.fn.fadeOut
$.fn.slideDown
$.fn.slideUp
$.fn.slideToggle
Show or hide the selected elements with a vertical sliding motion, depending on whether the
$('h1').show();
jQuery.fx.speeds
jQuery has an object at jQuery.fx.speeds that contains the default speed, as well as settings
speeds: {
slow: 600,
fast: 200,
// Default speed
_default: 400
It is possible to override or add to this object. For example, you may want to change the default duration of
jQuery.fx.speeds.blazing = 100;
jQuery.fx.speeds.turtle = 2000;
Often, you'll want to run some code once an animation is done -- if you run it before the animation is done,
it may affect the quality of the animation, or it may remove elements that are part of the
animation. [Definition: Callback functions provide a way to register your interest in an event that will
happen in the future.] In this case, the event we'll be responding to is the conclusion of the animation.
Inside of the callback function, the keyword this refers to the element that the effect was called on; as we
did inside of event handler functions, we can turn it into a jQuery object via$(this).
Note that if your selection doesn't return any elements, your callback will never run! You can solve this
problem by testing whether your selection returned any elements; if not, you can just run the callback
immediately.
var cb = function() {
console.log('done!');
};
if ($thing.length) {
$thing.fadeIn(300, cb);
} else {
cb();
The$.fn.animate method lets you animate to a set value, or to a value relative to the current value.
$('div.funtimes').animate(
left : "+=50",
opacity : 0.25
},
300, // duration
});
Note
Color-related properties cannot be animated with $.fn.animate using jQuery out of the box. Color
animations can easily be accomplished by including the color plugin. We'll discuss using plugins later in
the book.
Easing
[Definition: Easing describes the manner in which an effect occurs -- whether the rate of change is steady,
or varies over the duration of the animation.] jQuery includes only two methods of easing: swing and
linear. If you want more natural transitions in your animations, various easing plugins are available.
As of jQuery 1.4, it is possible to do per-property easing when using the $.fn.animate method.
$('div.funtimes').animate(
300
);
Managing Effects
jQuery provides several tools for managing animations.
$.fn.stop
$.fn.delay
Wait the specified number of milliseconds before running the next animation.
$('h1').show(300).delay(1000).hide(300);
jQuery.fx.off
If this value is true, there will be no transition for animations; elements will immediately be set to
the target final state instead. This can be especially useful when dealing with older browsers; you
Exercises
Reveal Hidden Text
Open the file /exercises/index.html in your browser. Use the file/exercises/js/blog.js. Your task is to add
some interactivity to the blog section of the page. The spec for the feature is as follows:
• Clicking on a headline in the #blog div should slide down the excerpt paragraph
• Clicking on another headline should slide down its excerpt paragraph, and slide up any other
• Hovering over an item in the main menu should show that item's submenu items, if any.
To accomplish this, use the $.fn.hover method to add and remove a class from the submenu items to
control whether they're visible or hidden. (The file at /exercises/css/styles.cssincludes the "hover" class for
this purpose.)
Create a Slideshow
Open the file /exercises/index.html in your browser. Use the file/exercises/js/slideshow.js. Your task is to
take a plain semantic HTML page and enhance it with JavaScript by adding a slideshow.
2. Write code to cycle through the list items inside the element; fade one in, display it for a few
3. When you get to the end of the list, start again at the beginning.
For an extra challenge, create a navigation area under the slideshow that shows how many images there
are and which image you're currently viewing. (Hint: $.fn.prevAll will come in handy for this.)
Chapter 7. Ajax
Overview
The XMLHttpRequest method (XHR) allows browsers to communicate with the server without requiring a
page reload. This method, also known as Ajax (Asynchronous JavaScript and XML), allows for web pages
Ajax requests are triggered by JavaScript code; your code sends a request to a URL, and when it receives a
response, a callback function can be triggered to handle the response. Because the request is
asynchronous, the rest of your code continues to execute while the request is being processed, so it’s
Most jQuery applications don’t in fact use XML, despite the name “Ajax”; instead, they transport data as
In general, Ajax does not work across domains. Exceptions are services that provide JSONP (JSON with
Key Concepts
Proper use of Ajax-related jQuery methods requires understanding some key concepts first.
The two most common “methods” for sending a request to a server are GET and POST. It’s important to
The GET method should be used for non-destructive operations — that is, operations where you are only
“getting” data from the server, not changing data on the server. For example, a query to a search service
might be a GET request. GET requests may be cached by the browser, which can lead to unpredictable
behavior if you are not expecting it. GET requests generally send all of their data in a query string.
The POST method should be used for destructive operations — that is, operations where you are changing
data on the server. For example, a user saving a blog post should be a POST request. POST requests are
generally not cached by the browser; a query string can be part of the URL, but the data tends to be sent
Data Types
jQuery generally requires some instruction as to the type of data you expect to get back from an Ajax
request; in some cases the data type is specified by the method name, and in other cases it is provided as
text
script
json
For transporting JSON-formatted data, which can include strings, arrays, and objects
Note
As of jQuery 1.4, if the JSON data sent by your server isn't properly formatted, the request may
fail silently. See http://json.org for details on properly formatting JSON, but as a general rule, use
built-in language methods for generating JSON on the server to avoid syntax issues.
jsonp
xml
I am a strong proponent of using the JSON format in most cases, as it provides the most flexibility. It is
especially useful for sending both HTML and data at the same time.
A is for Asynchronous
The asynchronicity of Ajax catches many new jQuery users off guard. Because Ajax calls are asynchronous
by default, the response is not immediately available. Responses can only be handled using a callback. So,
var response;
console.log(response); // undefined!
Instead, we need to pass a callback function to our request; this callback will run when the request
succeeds, at which point we can access the data that it returned, if any.
domain as the page making the request. This limitation does not apply to scripts that are loaded via
The other exception is requests targeted at a JSONP service on another domain. In the case of JSONP, the
provider of the service has agreed to respond to your request with a script that can be loaded into the
page using a <script> tag, thus avoiding the same-origin limitation; that script will include the data you
Firebug (or the Webkit Inspector in Chrome or Safari) is an invaluable tool for working with Ajax requests.
You can see Ajax requests as they happen in the Console tab of Firebug (and in the Resources > XHR
panel of Webkit Inspector), and you can click on a request to expand it and see details such as the request
headers, response headers, response content, and more. If something isn't going as expected with an Ajax
request, this is the first place to look to track down what's wrong.
all of them, and understanding it is imperative. We'll review it first, and then touch briefly on the
convenience methods.
I generally use the $.ajax method and do not use convenience methods. As you'll see, it offers features
that the convenience methods do not, and its syntax is more easily understandable, in my opinion.
$.ajax
jQuery’s core $.ajax method is a powerful and straightforward way of creating Ajax requests. It takes a
configuration object that contains all the instructions jQuery requires to complete the request.
The $.ajax method is particularly valuable because it offers the ability to specify both success and failure
callbacks. Also, its ability to take a configuration object that can be defined separately makes it easier to
visit http://api.jquery.com/jQuery.ajax/.
$.ajax({
url : 'post.php',
// the data to send
data : { id : 123 },
type : 'GET',
dataType : 'json',
success : function(json) {
$('<h1/>').text(json.title).appendTo('body');
$('<div class="content"/>')
.html(json.html).appendTo('body');
},
});
Note
A note about the dataType setting: if the server sends back data that is in a different format than you
specify, your code may fail, and the reason will not always be clear, because the HTTP response code will
not show an error. When working with Ajax requests, make sure your server is sending back the data type
you're asking for, and verify that the Content-type header is accurate for the data type. For example, for
options, visit http://api.jquery.com/jQuery.ajax/; here are several that you will use frequently:
async
Set to false if the request should be sent synchronously. Defaults to true. Note that if you set this
option to false, your request will block execution of other code until the response is received.
cache
Whether to use a cached response if available. Defaults to true for all dataTypes except "script"
and "jsonp". When set to false, the URL will simply have a cachebusting parameter appended to
it.
complete
A callback function to run when the request is complete, regardless of success or failure. The
function receives the raw request object and the text status of the request.
context
The scope in which the callback function(s) should run (i.e. what this will mean inside the callback
function(s)). By default, this inside the callback function(s) refers to the object originally passed
to $.ajax.
data
The data to be sent to the server. This can either be an object or a query string, such
asfoo=bar&baz=bim.
dataType
The type of data you expect back from the server. By default, jQuery will look at the MIME type of
error
A callback function to run if the request results in an error. The function receives the raw request
jsonp
The callback name to send in a query string when making a JSONP request. Defaults to "callback".
success
A callback function to run if the request succeeds. The function receives the response data
(converted to a JavaScript object if the dataType was JSON), as well as the text status of the
timeout
traditional
Set to true to use the param serialization style in use prior to jQuery 1.4. For details,
seehttp://api.jquery.com/jQuery.param/.
type
The type of the request, "POST" or "GET". Defaults to "GET". Other request types, such as "PUT"
and "DELETE" can be used, but they may not be supported by all browsers.
url
The url option is the only required property of the $.ajax configuration object; all other properties are
optional.
Convenience Methods
If you don't need the extensive configurability of $.ajax, and you don't care about handling errors, the Ajax
convenience functions provided by jQuery can be useful, terse ways to accomplish Ajax requests. These
methods are just "wrappers" around the core $.ajax method, and simply pre-set some of the options on
$.get
$.post
$.getScript
$.getJSON
url
data
The data to be sent to the server. Optional. This can either be an object or a query string, such
asfoo=bar&baz=bim.
Note
success callback
A callback function to run if the request succeeds. Optional. The function receives the response
data (converted to a JavaScript object if the data type was JSON), as well as the text status of the
data type
The type of data you expect back from the server. Optional.
Note
This option is only applicable for methods that don't already specify the data type in their name.
console.log(resp);
});
$.getScript('/static/js/myScript.js', function() {
functionFromMyScript();
});
$.getJSON('/details.php', function(resp) {
$.each(resp, function(k, v) {
});
});
$.fn.load
The $.fn.load method is unique among jQuery’s Ajax methods in that it is called on a selection.
The $.fn.load method fetches HTML from a URL, and uses the returned HTML to populate the selected
element(s). In addition to providing a URL to the method, you can optionally provide a selector; jQuery will
$('#newContent').load('/foo.html');
well-tested tool for adding Ajax capabilities to forms, and you should generally use it for handling forms
with Ajax rather than trying to roll your own solution for anything remotely complex. That said, there are a
two jQuery methods you should know that relate to form processing in jQuery:
$('#myForm').serialize();
$('#myForm').serializeArray();
powerful mashups of content. Many prominent sites provide JSONP services, allowing you access to their
content via a predefined API. A particularly great source of JSONP-formatted data is theYahoo! Query
Language, which we'll use in the following example to fetch news about cats.
$.ajax({
url : 'http://query.yahooapis.com/v1/public/yql',
jsonp : 'callback',
dataType : 'jsonp',
data : {
format : 'json'
},
success : function(response) {
console.log(response);
}
});
jQuery handles all the complex aspects of JSONP behind-the-scenes -- all we have to do is tell jQuery the
name of the JSONP callback parameter specified by YQL ("callback" in this case), and otherwise the whole
Ajax Events
Often, you’ll want to perform an operation whenever an Ajax requests starts or stops, such as showing or
hiding a loading indicator. Rather than defining this behavior inside every Ajax request, you can bind Ajax
events to elements just like you'd bind other events. For a complete list of Ajax events,
visit http://docs.jquery.com/Ajax_Events.
$('#loading_indicator')
.ajaxStart(function() { $(this).show(); })
Exercises
Load External Content
Open the file /exercises/index.html in your browser. Use the file/exercises/js/load.js. Your task is to load the
content of a blog item when a user clicks on the title of the item.
1. Create a target div after the headline for each blog post and store a reference to it on the
2. Bind a click event to the headline that will use the $.fn.load method to load the appropriate
content from /exercises/data/blog.html into the target div. Don't forget to prevent the default
Note that each blog headline in index.html includes a link to the post. You'll need to leverage the href of
that link to get the proper content from blog.html. Once you have the href, here's one way to process it
Remember to make liberal use of console.log to make sure you're on the right path!
Load Content Using JSON
Open the file /exercises/index.html in your browser. Use the file/exercises/js/specials.js. Your task is to
show the user details about the special for a given day when the user selects a day from the select
dropdown.
1. Append a target div after the form that's inside the #specials element; this will be where you
2. Bind to the change event of the select element; when the user changes the selection, send
3. When the request returns a response, use the value the user selected in the select (hint:
4. Add some HTML about the special to the target div you created.
5. Finally, because the form is now Ajax-enabled, remove the submit button from the form.
Note that we're loading the JSON every time the user changes their selection. How could we change the
code so we only make the request once, and then use a cached response when the user changes their
Chapter 8. Plugins
What exactly is a plugin?
A jQuery plugin is simply a new method that we use to extend jQuery's prototype object. By extending the
prototype object you enable all jQuery objects to inherit any methods that you add. As established,
whenever you call jQuery() you're creating a new jQuery object, with all of jQuery's methods inherited.
The idea of a plugin is to do something with a collection of elements. You could consider each method that
You can make your own plugins and use them privately in your code or you can release them into the wild.
There are thousands of jQuery plugins available online. The barrier to creating a plugin of your own is so
(function($){
$.fn.myNewPlugin = function() {
return this.each(function(){
// do something
});
};
}(jQuery));
Don't let that confuse you though. The point of a jQuery plugin is to extend jQuery's prototype object, and
(function($){
//...
}(jQuery));
This has the effect of creating a "private" scope that allows us to extend jQuery using the dollar symbol
without having to risk the possibility that the dollar has been over-written by another library.
$.fn.myNewPlugin = function() {
return this.each(function(){
// do something
});
};
The this keyword within the new plugin refers to the jQuery object on which the plugin is being called.
$.fn.myNewPlugin = function() {
};
Your typical jQuery object will contain references to any number of DOM elements, and that's why jQuery
So, to do something with a collection we need to loop through it, which is most easily achieved using
$.fn.myNewPlugin = function() {
return this.each(function(){
});
};
jQuery's each() method, like most other jQuery methods, returns a jQuery object, thus enabling what we've
all come to know and love as 'chaining' ($(...).css().attr()...). We wouldn't want to break this convention so
we return the this object. Within this loop you can do whatever you want with each element. Here's an
(function($){
$.fn.showLinkLocation = function() {
return this.filter('a').each(function(){
$(this).append(
);
});
};
}(jQuery));
// Usage example:
$('a').showLinkLocation();
This handy plugin goes through all anchors in the collection and appends the href attribute in brackets.
<a href="page.html">Foo</a>
(function($){
$.fn.showLinkLocation = function() {
return this.filter('a').append(function(){
});
};
}(jQuery));
We're using the append method's capability to accept a callback, and the return value of that callback will
determine what is appended to each element in the collection. Notice also that we're not using
the attr method to retrieve the href attribute, because the native DOM API gives us easy access with the
Here's another example of a plugin. This one doesn't require us to loop through every elememt with
the each() method. Instead, we're simply going to delegate to other jQuery methods directly:
(function($){
$(this).addClass(className);
});
};
}(jQuery));
// Usage example:
$('a').fadeInAndAddClass(400, 'finishedFading');
extensive plugin ecosystem. From table sorting to form validation to autocompletion ... if there’s a need
for it, chances are good that someone has written a plugin for it.
The quality of jQuery plugins varies widely. Many plugins are extensively tested and well-maintained, but
others are hastily created and then ignored. More than a few fail to follow best practices.
Google is your best initial resource for locating plugins, though the jQuery team is working on an improved
plugin repository. Once you’ve identified some options via a Google search, you may want to consult the
jQuery mailing list or the #jquery IRC channel to get input from others.
When looking for a plugin to fill a need, do your homework. Ensure that the plugin is well-documented, and
look for the author to provide lots of examples of its use. Be wary of plugins that do far more than you
need; they can end up adding substantial overhead to your page. For more tips on spotting a subpar
Once you choose a plugin, you’ll need to add it to your page. Download the plugin, unzip it if necessary,
place it your application’s directory structure, then include the plugin in your page using a script tag (after
Writing Plugins
Sometimes you want to make a piece of functionality available throughout your code; for example,
perhaps you want a single method you can call on a jQuery selection that performs a series of operations
Most plugins are simply methods created in the $.fn namespace. jQuery guarantees that a method called
on a jQuery object will be able to access that jQuery object as this inside the method. In return, your plugin
needs to guarantee that it returns the same object it received, unless explicitly documented otherwise.
(function($){
$.fn.hoverClass = function(c) {
return this.hover(
function() { $(this).toggleClass(c); }
);
};
}(jQuery);
For more on plugin development, read Mike Alsup's essential post, A Plugin Development Pattern. In it, he
creates a plugin called $.fn.hilight, which provides support for the metadata plugin if it's present, and
provides a centralized method for setting global and instance options for the plugin.
//
// create closure
//
(function($) {
//
// plugin definition
//
$.fn.hilight = function(options) {
debug(this);
return this.each(function() {
$this = $(this);
$this.css({
backgroundColor: o.background,
color: o.foreground
});
markup = $.fn.hilight.format(markup);
$this.html(markup);
});
};
//
//
function debug($obj) {
};
//
//
$.fn.hilight.format = function(txt) {
};
//
// plugin defaults
//
$.fn.hilight.defaults = {
foreground: 'red',
background: 'yellow'
};
//
// end of closure
//
})(jQuery);
This section is based, with permission, on the blog post Building Stateful jQuery Plugins by Scott Gonzalez.
While most existing jQuery plugins are stateless — that is, we call them on an element and that is the
extent of our interaction with the plugin — there’s a large set of functionality that doesn’t fit into the basic
plugin pattern.
In order to fill this gap, jQuery UI has implemented a more advanced plugin system. The new system
manages state, allows multiple functions to be exposed via a single plugin, and provides various extension
points. This system is called the widget factory and is exposed asjQuery.widget as part of jQuery UI 1.8;
To demonstrate the capabilities of the widget factory, we'll build a simple progress bar plugin.
To start, we’ll create a progress bar that just lets us set the progress once. As we can see below, this is
done by calling jQuery.widget with two parameters: the name of the plugin to create and an object literal
containing functions to support our plugin. When our plugin gets called, it will create a new plugin instance
and all functions will be executed within the context of that instance. This is different from a standard
jQuery plugin in two important ways. First, the context is an object, not a DOM element. Second, the
$.widget("nmk.progressbar", {
_create: function() {
this.element
.addClass("progressbar")
.text(progress);
});
The name of the plugin must contain a namespace; in this case we’ve used the nmk namespace. There is a
limitation that namespaces be exactly one level deep — that is, we can't use a namespace like nmk.foo.
We can also see that the widget factory has provided two properties for us.this.element is a jQuery object
containing exactly one element. If our plugin is called on a jQuery object containing multiple elements, a
separate plugin instance will be created for each element, and each instance will have its
own this.element. The second property, this.options, is a hash containing key/value pairs for all of our
plugin’s options. These options can be passed to our plugin as shown here.
Note
In our example we use the nmk namespace. The ui namespace is reserved for official jQuery UI plugins.
When building your own plugins, you should create your own namespace. This makes it clear where the
$("<div></div>")
.appendTo( "body" )
a standard plugin). The name of the function it adds is based on the name you pass to jQuery.widget,
without the namespace; in our case it will createjQuery.fn.progressbar. The options passed to our plugin
get set in this.options inside of our plugin instance. As shown below, we can specify default values for any
of our options. When designing your API, you should figure out the most common use case for your plugin
so that you can set appropriate default values and make all options truly optional.
$.widget("nmk.progressbar", {
// default options
options: {
value: 0
},
_create: function() {
this.element
.addClass( "progressbar" )
.text( progress );
});
Now that we can initialize our progress bar, we’ll add the ability to perform actions by calling methods on
our plugin instance. To define a plugin method, we just include the function in the object literal that we
pass to jQuery.widget. We can also define “private” methods by prepending an underscore to the function
name.
$.widget("nmk.progressbar", {
options: {
value: 0
},
_create: function() {
this.element
.addClass("progressbar")
.text(progress);
},
value: function(value) {
return this.options.value;
} else {
this.options.value = this._constrain(value);
},
_constrain: function(value) {
value = 100;
if (value < 0) {
value = 0;
return value;
});
To call a method on a plugin instance, you pass the name of the method to the jQuery plugin. If you are
calling a method that accepts parameters, you simply pass those parameters after the method name.
.appendTo("body")
alert(bar.progressbar("value"));
bar.progressbar("value", 50);
alert(bar.progressbar("value"));
Note
Executing methods by passing the method name to the same jQuery function that was used to initialize
the plugin may seem odd. This is done to prevent pollution of the jQuery namespace while maintaining the
One of the methods that is automatically available to our plugin is the option method. The option method
allows you to get and set options after initialization. This method works exactly like jQuery’s css and attr
methods: you can pass just a name to use it as a setter, a name and value to use it as a single setter, or a
hash of name/value pairs to set multiple values. When used as a getter, the plugin will return the current
value of the option that corresponds to the name that was passed in. When used as a setter, the
plugin’s _setOption method will be called for each option that is being set. We can specify
$.widget("nmk.progressbar", {
options: {
value: 0
},
_create: function() {
this.element.addClass("progressbar");
this._update();
},
this.options[key] = value;
this._update();
},
_update: function() {
this.element.text(progress);
});
Adding Callbacks
One of the easiest ways to make your plugin extensible is to add callbacks so users can react when the
state of your plugin changes. We can see below how to add a callback to our progress bar to signify when
the progress has reached 100%. The _trigger method takes three parameters: the name of the callback, a
native event object that initiated the callback, and a hash of data relevant to the event. The callback name
is the only required parameter, but the others can be very useful for users who want to implement custom
functionality on top of your plugin. For example, if we were building a draggable plugin, we could pass the
native mousemove event when triggering a drag callback; this would allow users to react to the drag
$.widget("nmk.progressbar", {
options: {
value: 0
},
_create: function() {
this.element.addClass("progressbar");
this._update();
},
this.options[key] = value;
this._update();
},
_update: function() {
this.element.text(progress);
if (this.options.value == 100) {
});
Callback functions are essentially just additional options, so you can get and set them just like any other
option. Whenever a callback is executed, a corresponding event is triggered as well. The event type is
determined by concatenating the plugin name and the callback name. The callback and event both receive
the same two parameters: an event object and a hash of data relevant to the event, as we’ll see below.
If your plugin has functionality that you want to allow the user to prevent, the best way to support this is
by creating cancelable callbacks. Users can cancel a callback, or its associated event, the same way they
cancel any native event: by calling event.preventDefault() or using return false. If the user cancels the
callback, the _trigger method will return false so you can implement the appropriate functionality within
your plugin.
.appendTo("body")
.progressbar({
})
});
When you call jQuery.widget, it creates a constructor function for your plugin and sets the object literal that you pass in as the
prototype for your plugin instances. All of the functionality that automatically gets added to your plugin comes from a base widget
prototype, which is defined as jQuery.Widget.prototype. When a plugin instance is created, it is stored on the original DOM element
Because the plugin instance is directly linked to the DOM element, you can access the plugin instance directly instead of going
through the exposed plugin method if you want. This will allow you to call methods directly on the plugin instance instead of
passing method names as strings and will also give you direct access to the plugin’s properties.
.appendTo("body")
.progressbar()
.data("progressbar" );
bar.option("value", 50);
alert(bar.options.value);
One of the biggest benefits of having a constructor and prototype for a plugin is the ease of extending the plugin. By adding or
modifying methods on the plugin’s prototype, we can modify the behavior of all instances of our plugin. For example, if we wanted
to add a method to our progress bar to reset the progress to 0% we could add this method to the prototype and it would instantly
$.nmk.progressbar.prototype.reset = function() {
this._setOption("value", 0);
};
Cleaning Up
In some cases, it will make sense to allow users to apply and then later unapply your plugin. You can
accomplish this via the destroy method. Within the destroy method, you should undo anything your plugin
may have done during initialization or later use. The destroy method is automatically called if the element
that your plugin instance is tied to is removed from the DOM, so this can be used for garbage collection as
well. The default destroy method removes the link between the DOM element and the plugin instance, so
it’s important to call the base function from your plugin’sdestroy method.
$.widget( "nmk.progressbar", {
options: {
value: 0
},
_create: function() {
this.element.addClass("progressbar");
this._update();
},
this.options[key] = value;
this._update();
},
_update: function() {
this.element.text(progress);
if (this.options.value == 100 ) {
},
destroy: function() {
this.element
.removeClass("progressbar")
.text("");
$.Widget.prototype.destroy.call(this);
});
Conclusion
The widget factory is only one way of creating stateful plugins. There are a few different models that can
be used and each have their own advantages and disadvantages. The widget factory solves lots of
common problems for you and can greatly improve productivity, it also greatly improves code reuse,
making it a great fit for jQuery UI as well as many other stateful plugins.
Exercises
Make a Table Sortable
For this exercise, your task is to identify, download, and implement a table sorting plugin on the index.html
page. When you’re done, all columns in the table on the page should be sortable.
plugin called "stripe" that you can call on any table element. When the plugin is called on a table element,
it should change the color of odd rows in the table body to a user-specified color.
$('#myTable').stripe('#cccccc');
Don't forget to return the table so other methods can be chained after the plugin!
best practices in this chapter are based on the jQuery Anti-Patterns for Performancepresentation by Paul
Irish.
// do stuff
at a time.
// this is bad
$('#ballers').append(newListItem);
});
// better: do this
frag.appendChild(newListItem);
});
$('#ballers')[0].appendChild(frag);
// or do this
});
$('#ballers').html(myHtml);
// BAD
if ($eventfade.data('currently') != 'showing') {
$eventfade.stop();
if ($eventhover.data('currently') != 'showing') {
$eventhover.stop();
if ($spans.data('currently') != 'showing') {
$spans.stop();
// GOOD!!
$.each($elems, function(i,elem) {
if (elem.data('currently') != 'showing') {
elem.stop();
});
Instead, use an object literal to organize and name your handlers and callbacks.
// BAD
$(document).ready(function() {
$('#magic').click(function(e) {
$('#yayeffects').slideUp(function() {
// ...
});
});
// ...
});
});
// BETTER
var PI = {
onReady : function() {
$('#magic').click(PI.candyMtn);
},
candyMtn : function(e) {
$('#yayeffects').slideUp(PI.slideCb);
},
};
$(document).ready(PI.onReady);
Optimize Selectors
Selector optimization is less important than it used to be, as more browser
implementdocument.querySelectorAll() and the burden of selection shifts from jQuery to the browser.
// fast
$('#container div.robotarm');
// super-fast
$('#container').find('div.robotarm');
The $.fn.find approach is faster because the first selection is handled without going through the Sizzle
selector engine — ID-only selections are handled using document.getElementById(), which is extremely
Be specific on the right-hand side of your selector, and less specific on the left.
// unoptimized
$('div.data .gonzalez');
// optimized
$('.data td.gonzalez');
Use tag.class if possible on your right-most selector, and just tag or just .class on the left.
$('.data td.gonzalez');
A "flatter" DOM also helps improve selector performance, as the selector engine has fewer layers to
Selections that specify or imply that a match could be found anywhere can be very slow.
list) instead of multiple contained elements (for example, list items). jQuery makes this easy with $.fn.live
and $.fn.delegate. Where possible, you should use $.fn.delegate instead of$.fn.live, as it eliminates the
need for an unnecessary selection, and its explicit context (vs.$.fn.live's context of document) reduces
In addition to performance benefits, event delegation also allows you to add new contained elements to
your page without having to re-bind the event handlers for them as they're added.
$('li.trigger').click(handlerFn);
$('li.trigger').live('click', handlerFn);
version 1.4 to help address this issue, allowing you to remove an element from the DOM while you work
with it.
$table.detach();
$parent.append(table);
Use Stylesheets for Changing CSS on Many
Elements
If you're changing the CSS of more than 20 elements using $.fn.css, consider adding a style tag to the
$('a.swedberg').css('color', '#asd123');
.appendTo('head');
faster. Be sure you understand the difference between a DOM element and a jQuery selection before doing
this, though.
// regular
$(elem).data(key,value);
// 10x faster
$.data(elem,key,value);
though nothing's wrong. It's up to you to verify that your selection contains some elements.
// in the selection
$('#nosuchthing').slideUp();
// Better
if ($mySelection.length) { mySelection.slideUp(); }
jQuery.fn.doOnce = function(func){
return this;
$('li.cartitems').doOnce(function(){
});
This guidance is especially applicable for jQuery UI widgets, which have a lot of overhead even when the
Variable Definition
Variables can be defined in one statement instead of several.
var test = 1;
var test = 1,
test3 = test2(test);
Conditionals
// old way
// better
if (/^(foo|bar)$/.test(type)) { ... }
full-blown client-side applications, you need to consider how to organize your code. In this chapter, we'll
take a look at various code organization patterns you can use in your jQuery application and explore the
Before we jump into code organization patterns, it's important to understand some concepts that are
• Your code should be divided into units of functionality — modules, services, etc. Avoid the
temptation to have all of your code in one huge $(document).ready() block. This concept, loosely,
is known as encapsulation.
• Don't repeat yourself. Identify similarities among pieces of functionality, and use inheritance
• Despite jQuery's DOM-centric nature, JavaScript applications are not all about the DOM.
Remember that not all pieces of functionality need to — or should — have a DOM representation.
exist on its own, and communication between units should be handled via a messaging system
such as custom events or pub/sub. Stay away from direct communication between units of
The concept of loose coupling can be especially troublesome to developers making their first foray into
Encapsulation
The first step to code organization is separating pieces of your application into distinct pieces; sometimes,
An object literal is perhaps the simplest way to encapsulate related code. It doesn't offer any privacy for
properties or methods, but it's useful for eliminating anonymous functions from your code, centralizing
var myFeature = {
myProperty : 'hello',
myMethod : function() {
console.log(myFeature.myProperty);
},
init : function(settings) {
myFeature.settings = settings;
},
readSettings : function() {
console.log(myFeature.settings);
};
myFeature.myProperty; // 'hello'
The object literal above is simply an object assigned to a variable. The object has one property and several
methods. All of the properties and methods are public, so any part of your application can see the
properties and call methods on the object. While there is an init method, there's nothing requiring that it
jQuery style:
$(document).ready(function() {
$('#myFeature li')
.append('<div/>')
.click(function() {
$div.load('foo.php?item=' +
$this.attr('id'),
function() {
$div.show();
$this.siblings()
.find('div').hide();
);
});
});
If this were the extent of our application, leaving it as-is would be fine. On the other hand, if this was a
piece of a larger application, we'd do well to keep this functionality separate from unrelated functionality.
We might also want to move the URL out of the code and into a configuration area. Finally, we might want
to break up the chain to make it easier to modify pieces of the functionality later.
Example 10.2. Using an object literal for a jQuery feature
var myFeature = {
init : function(settings) {
myFeature.config = {
urlBase : '/foo.php?item='
};
$.extend(myFeature.config, settings);
myFeature.setup();
},
setup : function() {
myFeature.config.$items
.each(myFeature.createContainer)
.click(myFeature.showItem);
},
createContainer : function() {
var $i = $(this),
$c = myFeature.config.$container.clone()
.appendTo($i);
$i.data('container', $c);
},
buildUrl : function() {
return myFeature.config.urlBase +
myFeature.$currentItem.attr('id');
},
showItem : function() {
myFeature.getContent(myFeature.showContent);
},
getContent : function(callback) {
myFeature.$currentItem
.data('container').load(url, callback);
},
showContent : function() {
myFeature.$currentItem
.data('container').show();
myFeature.hideContent();
},
hideContent : function() {
myFeature.$currentItem.siblings()
.each(function() {
$(this).data('container').hide();
});
};
$(document).ready(myFeature.init);
The first thing you'll notice is that this approach is obviously far longer than the original — again, if this
were the extent of our application, using an object literal would likely be overkill. Assuming it's not the
• We've broken our feature up into tiny methods. In the future, if we want to change how
content is shown, it's clear where to change it. In the original code, this step is much harder to
locate.
• We've moved configuration options out of the body of the code and put them in a central
location.
• We've eliminated the constraints of the chain, making the code easier to refactor, remix, and
rearrange.
For non-trivial features, object literals are a clear improvement over a long stretch of code stuffed in a $
(document).ready() block, as they get us thinking about the pieces of our functionality. However, they
aren't a whole lot more advanced than simply having a bunch of function declarations inside of that $
(document).ready() block.
The module pattern overcomes some of the limitations of the object literal, offering privacy for variables
changePrivateThing = function() {
},
sayPrivateThing = function() {
console.log(privateThing);
changePrivateThing();
};
// public API
return {
publicThing : publicThing,
sayPrivateThing : sayPrivateThing
})();
feature.sayPrivateThing();
// of privateThing
In the example above, we self-execute an anonymous function that returns an object. Inside of the
function, we define some variables. Because the variables are defined inside of the function, we don't have
access to them outside of the function unless we put them in the return object. This means that no code
outside of the function has access to the privateThing variable or to thechangePrivateThing function.
However, sayPrivateThing does have access toprivateThing and changePrivateThing, because both were
This pattern is powerful because, as you can gather from the variable names, it can give you private
variables and functions while exposing a limited API consisting of the returned object's properties and
methods.
Below is a revised version of the previous example, showing how we could create the same feature using
the module pattern while only exposing one public method of the module,showItemByIndex().
$(document).ready(function() {
var feature = (function() {
$currentItem,
urlBase = '/foo.php?item=',
createContainer = function() {
var $i = $(this),
$c = $container.clone().appendTo($i);
$i.data('container', $c);
},
buildUrl = function() {
},
showItem = function() {
getContent(showContent);
},
showItemByIndex = function(idx) {
$.proxy(showItem, $items.get(idx));
},
getContent = function(callback) {
$currentItem.data('container').load(buildUrl(), callback);
},
showContent = function() {
$currentItem.data('container').show();
hideContent();
},
hideContent = function() {
$currentItem.siblings()
.each(function() {
$(this).data('container').hide();
});
};
$items
.each(createContainer)
.click(showItem);
return { showItemByIndex : showItemByIndex };
})();
feature.showItemByIndex(0);
});
Managing Dependencies
Note
athttp://requirejs.org/docs/jquery.html, and is used with the permission of RequireJS author James Burke.
When a project reaches a certain size, managing the script modules for a project starts to get tricky. You
need to be sure to sequence the scripts in the right order, and you need to start seriously thinking about
combining scripts together into a bundle for deployment, so that only one or a very small number of
requests are made to load the scripts. You may also want to load code on the fly, after page load.
RequireJS, a dependency management tool by James Burke, can help you manage the script modules, load
them in the right order, and make it easy to combine the scripts later via the RequireJS optimization tool
without needing to change your markup. It also gives you an easy way to load scripts after the page has
loaded, allowing you to spread out the download size over time.
RequireJS has a module system that lets you define well-scoped modules, but you do not have to follow
that system to get the benefits of dependency management and build-time optimizations. Over time, if
you start to create more modular code that needs to be reused in a few places, the module format for
RequireJS makes it easy to write encapsulated code that can be loaded on the fly. It can grow with you,
particularly if you want to incorporate internationalization (i18n) string bundles, to localize your project for
different languages, or load some HTML strings and make sure those strings are available before executing
Getting RequireJS
The easiest way to use RequireJS with jQuery is to download a build of jQuery that has RequireJS built in.
This build excludes portions of RequireJS that duplicate jQuery functionality. You may also find it useful to
Using RequireJS in your page is simple: just include the jQuery that has RequireJS built in, then require
your application files. The following example assumes that the jQuery build, and your other scripts, are all
in a scripts/ directory.
<!DOCTYPE html>
<html>
<head>
<script src="scripts/require-jquery.js"></script>
<script>require(["app"]);</script>
</head>
<body>
</body>
</html>
The call to require(["app"]) tells RequireJS to load the scripts/app.js file. RequireJS will load any
dependency that is passed to require() without a .js extension from the same directory as require-jquery.js,
though this can be configured to behave differently. If you feel more comfortable specifying the whole
<script>require(["scripts/app.js"]);</script>
What is in app.js? Another call to require.js to load all the scripts you need and any init work you want to
do for the page. This example app.js script loads two plugins, jquery.alpha.jsand jquery.beta.js (not the
names of real plugins, just an example). The plugins should be in the same directory as require-jquery.js:
$(function() {
$('body').alpha().beta();
});
});
RequireJS makes it easy to define reusable modules via require.def(). A RequireJS module can have
dependencies that can be used to define a module, and a RequireJS module can return a value — an
object, a function, whatever — that can then be consumed by yet other modules.
If your module does not have any dependencies, then just specify the name of the module as the first
argument to require.def(). The second argument is just an object literal that defines the module's
require.def("my/simpleshirt",
color: "black",
size: "unisize"
);
This example would be stored in a my/simpleshirt.js file.
If your module has dependencies, you can specify the dependencies as the second argument
torequire.def() (as an array) and then pass a function as the third argument. The function will be called to
define the module once all dependencies have loaded. The function receives the values returned by the
dependencies as its arguments (in the same order they were required in the array), and the function
require.def("my/shirt",
["my/cart", "my/inventory"],
function(cart, inventory) {
return {
color: "blue",
size: "large"
addToCart: function() {
inventory.decrement(this);
cart.add(this);
);
In this example, a my/shirt module is created. It depends on my/cart and my/inventory. On disk, the files
my/inventory.js
my/shirt.js
The function that defines my/shirt is not called until the my/cart and my/inventorymodules have been
loaded, and the function receives the modules as the cart and inventoryarguments. The order of the
function arguments must match the order in which the dependencies were required in the dependencies
array. The object returned by the function call defines the my/shirt module. Be defining modules in this
way, my/shirt does not exist as a global object. Modules that define globals are explicitly discouraged, so
Modules do not have to return objects; any valid return value from a function is allowed.
require.def("my/title",
["my/dependency1", "my/dependency2"],
function(dep1, dep2) {
return function(title) {
);
easily. Download the RequireJS source and place it anywhere you like, preferrably somewhere outside your
web development area. For the purposes of this example, the RequireJS source is placed as a sibling to
the webapp directory, which contains the HTML page and the scripts directory with all the scripts.
webapp/app.html
webapp/scripts/app.js
webapp/scripts/require-jquery.js
webapp/scripts/jquery.alpha.js
webapp/scripts/jquery.beta.js
Then, in the scripts directory that has require-jquery.js and app.js, create a file called app.build.js with the
following contents:
appDir: "../",
baseUrl: "scripts/",
dir: "../../webapp-build",
optimize: "none",
modules: [
{
name: "app"
To use the build tool, you need Java 6 installed. Closure Compiler is used for the JavaScript minification
To start the build, go to the webapp/scripts directory, execute the following command:
# non-windows systems
../../requirejs/build/build.sh app.build.js
# windows systems
..\..\requirejs\build\build.bat app.build.js
the app.js contents, jquery.alpha.jsand jquery.beta.js inlined. If you then load the app.html file in
the webapp-builddirectory, you should not see any network requests for jquery.alpha.js andjquery.beta.js.
Exercises
Create a Portlet Module
Open the file /exercises/portlets.html in your browser. Use the file/exercises/js/portlets.js. Your task is to
create a portlet creation function that uses the module pattern, such that the following code will work:
title : 'Curry',
source : 'data/html/curry.html',
});
myPortlet.$element.appendTo('body');
Each portlet should be a div with a title, a content area, a button to open/close the portlet, a button to
remove the portlet, and a button to refresh the portlet. The portlet returned by the Portlet function should
myPortlet.setSource('data/html/onions.html');
to as a user interacts with the browser. Custom events open up a whole new world of event-driven
programming. In this chapter, we’ll use jQuery’s custom events system to make a simple Twitter search
application.
It can be difficult at first to understand why you'd want to use custom events, when the built-in events
seem to suit your needs just fine. It turns out that custom events offer a whole new way of thinking about
event-driven JavaScript. Instead of focusing on the element that triggers an action, custom events put the
spotlight on the element being acted upon. This brings a bevy of benefits, including:
• Behaviors of the target element can easily be triggered by different elements using the same
code.
Why should you care? An example is probably the best way to explain. Suppose you have a lightbulb in a
room in a house. The lightbulb is currently turned on, and it’s controlled by two three-way switches and a
clapper:
<div class="switch"></div>
<div class="switch"></div>
<div class="clapper"></div>
</div>
Triggering the clapper or either of the switches will change the state of the lightbulb. The switches and the
clapper don’t care what state the lightbulb is in; they just want to change the state.
Without custom events, you might write some code like this:
$('.switch, .clapper').click(function() {
if ($light.hasClass('on')) {
$light.removeClass('on').addClass('off');
} else {
$light.removeClass('off').addClass('on');
});
With custom events, your code might look more like this:
$('.lightbulb').bind('changeState', function(e) {
var $light = $(this);
if ($light.hasClass('on')) {
$light.removeClass('on').addClass('off');
} else {
$light.removeClass('off').addClass('on');
});
$('.switch, .clapper').click(function() {
$(this).parent().find('.lightbulb').trigger('changeState');
});
This last bit of code is not that exciting, but something important has happened: we’ve moved the
behavior of the lightbulb to the lightbulb, and away from the switches and the clapper.
Let’s make our example a little more interesting. We’ll add another room to our house, along with a master
<div class="switch"></div>
<div class="switch"></div>
<div class="clapper"></div>
</div>
<div class="switch"></div>
<div class="switch"></div>
<div class="clapper"></div>
</div>
<div id="master_switch"></div>
If there are any lights on in the house, we want the master switch to turn all the lights off; otherwise, we
want it to turn all lights on. To accomplish this, we’ll add two more custom events to the
lightbulbs: turnOn and turnOff. We’ll make use of them in the changeState custom event, and use some
$('.lightbulb')
.bind('changeState', function(e) {
if ($light.hasClass('on')) {
$light.trigger('turnOff');
} else {
$light.trigger('turnOn');
})
.bind('turnOn', function(e) {
$(this).removeClass('off').addClass('on');
})
.bind('turnOff', function(e) {
$(this).removeClass('off').addClass('on');
});
$('.switch, .clapper').click(function() {
$(this).parent().find('.lightbulb').trigger('changeState');
});
$('#master_switch').click(function() {
if ($('.lightbulb.on').length) {
$('.lightbulb').trigger('turnOff');
} else {
$('.lightbulb').trigger('turnOn');
});
Note how the behavior of the master switch is attached to the master switch; the behavior of a lightbulb
Note
If you’re accustomed to object-oriented programming, you may find it useful to think of custom events as
methods of objects. Loosely speaking, the object to which the method belongs is created via the jQuery
selector. Binding the changeState custom event to all $(‘.light’) elements is akin to having a class
called Light with a method of changeState, and then instantiating newLight objects for each element with
a classname of light.
Recap: $.fn.bind and $.fn.trigger
In the world of custom events, there are two important jQuery methods: $.fn.bind and $.fn.trigger. In the Events chapter, we saw
how to use these methods for working with user events; for this chapter, it's important to remember two things:
• The $.fn.bind method takes an event type and an event handling function as arguments. Optionally, it can also
receive event-related data as its second argument, pushing the event handling function to the third argument. Any data
that is passed will be available to the event handling function in the data property of the event object. The event
handling function always receives the event object as its first argument.
• The $.fn.trigger method takes an event type as its argument. Optionally, it can also take an array of values. These
values will be passed to the event handling function as arguments after the event object.
Here is an example of the usage of $.fn.bind and $.fn.trigger that uses custom data in both cases:
console.log(arg1); // 'bim'
console.log(arg2); // 'baz'
});
A Sample Application
To demonstrate the power of custom events, we’re going to create a simple tool for searching Twitter. The
tool will offer several ways for a user to add search terms to the display: by entering a search term in a
text box, by entering multiple search terms in the URL, and by querying Twitter for trending terms.
The results for each term will be shown in a results container; these containers will be able to be
<h1>Twitter Search</h1>
<form>
id="search_term" />
</form>
<div id="twitter">
<span class="search_term"></span></h2>
</div>
</div>
This gives us a container (#twitter) for our widget, a template for our results containers (hidden via CSS),
and a simple form where users can input a search term. (For the sake of simplicity, we’re going to assume
that our application is JavaScript-only and that our users will always have CSS.)
There are two types of objects we’ll want to act on: the results containers, and the Twitter container.
The results containers are the heart of the application. We’ll create a plugin that will prepare each results
container once it’s added to the Twitter container. Among other things, it will bind the custom events for
each container and add the action buttons at the top right of each container. Each results container will
refresh
Mark the container as being in the “refreshing” state, and fire the request to fetch the data for the
search term.
populate
Receive the returned JSON data and use it to populate the container.
remove
Remove the container from the page after the user verifies the request to do so. Verification can
be bypassed by passing true as the second argument to the event handler. The remove event
also removes the term associated with the results container from the global object containing the
search terms.
collapse
Add a class of collapsed to the container, which will hide the results via CSS. It will also turn the
expand
Remove the collapsed class from the container. It will also turn the container’s “Expand” button
The plugin is also responsible for adding the action buttons to the container. It binds a click event to each
action’s list item, and uses the list item’s class to determine which custom event will be triggered on the
$.fn.twitterResult = function(settings) {
return $(this).each(function() {
$actions = $.fn.twitterResult.actions =
$.fn.twitterResult.actions ||
$.fn.twitterResult.createActions(),
$a = $actions.clone().prependTo($results),
term = settings.term;
$results.find('span.search_term').text(term);
$.each(
function(i, ev) {
$results.bind(
ev,
{ term : term },
$.fn.twitterResult.events[ev]
);
);
$a.find('li').click(function() {
});
});
};
$.fn.twitterResult.createActions = function() {
'<li class="refresh">Refresh</li>' +
'<li class="remove">Remove</li>' +
'<li class="collapse">Collapse</li>'
);
};
$.fn.twitterResult.events = {
refresh : function(e) {
$this.find('p.tweet').remove();
$.getJSON(
'http://search.twitter.com/search.json?q=' +
escape(e.data.term) + '&rpp=5&callback=?',
function(json) {
);
},
$this.find('p.loading').remove();
$.each(results, function(i,result) {
'<a href="http://twitter.com/' +
result.from_user +
'">' +
result.from_user +
'</a>: ' +
result.text +
result.created_at +
'</span>' +
'</p>';
$this.append(tweet);
});
$this.removeClass('refreshing');
},
if (
!force &&
){
return;
$(this).remove();
search_terms[e.data.term] = 0;
},
collapse : function(e) {
$(this).find('li.collapse').removeClass('collapse')
.addClass('expand').text('Expand');
$(this).addClass('collapsed');
},
expand : function(e) {
$(this).find('li.expand').removeClass('expand')
.addClass('collapse').text('Collapse');
$(this).removeClass('collapsed');
};
The Twitter container itself will have just two custom events:
getResults
Receives a search term and checks to determine whether there’s already a results container for
the term; if not, adds a results container using the results template, set up the results container
using the $.fn.twitterResult plugin discussed above, and then triggers the refresh event on the
results container in order to actually load the results. Finally, it will store the search term so the
getTrends
Queries Twitter for the top 10 trending terms, then iterates over them and triggers
thegetResults event for each of them, thereby adding a results container for each term.
$('#twitter')
if (!search_terms[term]) {
$results = $template.clone().
removeClass('template').
insertBefore($this.find('div:first')).
twitterResult({
'term' : term
});
$results.trigger('refresh');
search_terms[term] = 1;
})
.bind('getTrends', function(e) {
$.getJSON('http://search.twitter.com/trends.json?callback=?', function(json) {
});
});
});
So far, we’ve written a lot of code that does approximately nothing, but that’s OK. By specifying all the
behaviors that we want our core objects to have, we’ve created a solid framework for rapidly building out
the interface.
Let’s start by hooking up our text input and the “Load Trending Terms” button. For the text input, we’ll
capture the term that was entered in the input and pass it as we trigger the Twitter
container’sgetResults event. Clicking the “Load Trending Terms” will trigger the Twitter
container’sgetTrends event:
$('form').submit(function(e) {
e.preventDefault();
});
$('#get_trends').click(function() {
$('#twitter').trigger('getTrends');
});
By adding a few buttons with the appropriate IDs, we can make it possible to remove, collapse, expand,
and refresh all results containers at once, as shown below. For the remove button, note how we’re passing
a value of true to the event handler as its second argument, telling the event handler that we don’t want
});
$('#remove').click(function(e) {
});
Conclusion
Custom events offer a new way of thinking about your code: they put the emphasis on the target of a
behavior, not on the element that triggers it. If you take the time at the outset to spell out the pieces of
your application, as well as the behaviors those pieces need to exhibit, custom events can provide a
powerful way for you to “talk” to those pieces, either one at a time or en masse. Once the behaviors of a
piece have been described, it becomes trivial to trigger those behaviors from anywhere, allowing for rapid
creation of and experimentation with interface options. Finally, custom events can enhance code
readability and maintainability, by making clear the relationship between an element and its behaviors.
You can see the full application at demos/custom-events.html and demos/js/custom-events.js in the
sample code.
Copyright Rebecca Murphey, released under the Creative Commons Attribution-Share Alike 3.0 United States license.