Learning jQuery 1.3
It began as a labor of love back in 2005 by John Resig, a JavaScript wunderkind who
now works for the Mozilla Corporation. Inspired by pioneers in the field such as Dean
Edwards and Simon Willison, Resig put together a set of functions to make it easy to
programmatically find elements on a web page and assign behaviors to them. By the time
he first publicly announced his project in January 2006, he had added DOM modification
and basic animations. He gave it the name jQuery to emphasize the central role of
finding, or "querying," parts of a web page and acting on them with JavaScript. In the
few short years since then, jQuery has grown in its feature set, improved in its
performance, and gained widespread adoption by some of the most popular sites on the
Internet. While Resig remains the lead developer of the project, jQuery has blossomed, in
true open-source fashion, to the point where it now boasts a core team of top-notch
JavaScript developers, as well as a vibrant community of thousands of developers.
The jQuery JavaScript Library can enhance your websites regardless of your background.
It provides a wide range of features, an easy-to-learn syntax, and robust cross-platform
compatibility in a single compact file. What's more, hundreds of plug-ins have been
developed to extend jQuery's functionality, making it an essential tool for nearly every
client-side scripting occasion.
Learning jQuery provides a gentle introduction to jQuery concepts, allowing you to add
interactions and animations to your pages—even if previous attempts at writing
JavaScript have left you baffled. This book guides you past the pitfalls associated with
AJAX, events, effects, and advanced JavaScript language features, and provides you with
a brief reference to the jQuery library to return to again and again.
What This Book Covers
In Chapter 1 you'll get your feet wet with the jQuery JavaScript library. The chapter
begins with a description of jQuery and what it can do for you. It walks you through
downloading and setting up the library, as well as writing your first script.
In Chapter 2 you'll learn how to use jQuery's selector expressions and DOM traversal
methods to find elements on the page, wherever they may be. You'll use jQuery to apply
styling to a diverse set of page elements, sometimes in a way that pure CSS cannot.
In Chapter 3 you'll use jQuery's event-handling mechanism to fire off behaviors when
browser events occur. You'll see how jQuery makes it easy to attach events to elements
unobtrusively, even before the page finishes loading. And, you'll be introduced to more
advanced topics, such as event bubbling, delegation, and namespacing.
In Chapter 4 you'll be introduced to jQuery's animation techniques and see how to hide,
show, and move page elements with effects that are both useful and pleasing to the eye.
In Chapter 5 you'll learn how to change your page on command. This chapter will teach
you how to alter the very structure of an HTML document, as well as its content, on
the fly.
In Chapter 6 you'll discover the many ways in which jQuery makes it easy to access
server-side functionality without resorting to clunky page refreshes.
In the next three chapters (7, 8, and 9) you'll work through several real-world examples,
pulling together what you've learned in previous chapters and creating robust jQuery
solutions to common problems.
In Chapter 7, "Table Manipulation," you'll sort, sift, and style information to create
beautiful and functional data layouts.
In Chapter 8, "Forms with Function," you'll master the finer points of client-side
validation, design an adaptive form layout, and implement interactive client-server form
features such as autocompletion.
In Chapter 9, "Shufflers and Rotators," you'll enhance the beauty and utility of page
elements by showing them in more manageable chunks. You'll make information fly in
and out of view both on its own and under user control.
Chapters 10 and 11 take you beyond the core jQuery methods to explore third-party
extensions to the library, and show you various ways you can extend the library yourself.
In Chapter 10, "Using Plug-ins," you'll examine the Form plug-in and the official
collection of user interface plug-ins known as jQuery UI. You'll also learn where to find
many other popular jQuery plug-ins and see what they can do for you.
In Chapter 11, "Developing Plug-ins," you'll learn how to take advantage of jQuery's
impressive extension capabilities to develop your own plug-ins from the ground up.
You'll create your own utility functions, add jQuery object methods, write custom
selector expressions, and more.
Effects
If actions speak louder than words, then in the JavaScript world, effects make actions
speak louder still. With jQuery, we can easily add impact to our actions through a set
of simple visual effects, and even craft our own, more sophisticated animations.
jQuery effects certainly add flair, as is evident when we see elements gradually slide
into view instead of appearing all at once. However, they can also provide important
usability enhancements that help orient the user when there is some change on a
page (especially common in AJAX applications). In this chapter, we will explore a
number of these effects and combine them in interesting ways.
Inline CSS modification
Before we jump into the nifty jQuery effects, a quick look at CSS is in order. In
previous chapters we have been modifying a document's appearance by defining
styles for classes in a separate stylesheet and then adding or removing those classes
with jQuery. Typically, this is the preferred process for injecting CSS into HTML
because it respects the stylesheet's role in dealing with the presentation of a page.
However, there may be times when we need to apply styles that haven't been, or
can't easily be, defined in a stylesheet. Fortunately, jQuery offers the .css() method
for such occasions.
This method acts as both a getter and a setter. To get the value of a style property, we
simply pass the name of the property as a string, like .css('backgroundColor').
Multi-word properties can be interpreted by jQuery when hyphenated, as they are
in CSS notation (background-color), or camel-cased, as they are in DOM notation
(backgroundColor). For setting style properties, the .css() method comes in two
flavors—one that takes a single style property and its value and one that takes a map
of property-value pairs:
.css('property','value')
.css({property1: 'value1', 'property-2': 'value2'})
Experienced JavaScript developers will recognize these jQuery maps as JavaScript
object literals.
Numeric values do not take quotation marks while string values do.
However, when using the map notation, quotation marks are not required
for property names if they are written in camel-cased DOM notation.
We use the .css() method the same way we've been using .addClass()
—by chaining it to a selector and binding it to an event. To demonstrate this,
we'll return to the style switcher example of Chapter 3, but with different HTML:
<div id="switcher">
<div class="label">Text Size</div>
<button id="switcher-default">Default</button>
<button id="switcher-large">Bigger</button>
<button id="switcher-small">Smaller</button>
</div>
<div class="speech">
<p>Fourscore and seven years ago our fathers brought forth
on this continent a new nation, conceived in liberty,
and dedicated to the proposition that all men are created
equal.</p>
</div>
By linking to a stylesheet with a few basic style rules, the page can initially look like
the following screenshot:
In this version of the style switcher, we're using <button> elements. Clicking
on the Bigger and Smaller buttons will increase or decrease the text size of
<div class="speech">, while clicking on the Default button will reset
<div class="speech"> to its original text size.
If all we wanted were to change the font size a single time to a predetermined value,
we could still use the .addClass() method. But let's suppose that now we want
the text to continue increasing or decreasing incrementally each time the respective
button is clicked. Although it might be possible to define a separate class for each
click and iterate through them, a more straightforward approach would be to
compute the new text size each time by getting the current size and increasing it by
a set factor (for example, 40%).
Our code will start with the $(document).ready() and $('#switcher-large').
click() event handlers:
$(document).ready(function() {
$('#switcher-large').click(function() {
});
});
Next, the font size can be easily discovered by using the .css() method: $('div.
speech').css('fontSize'). However, because the returned value will include
a trailing 'px', we'll need to strip that part in order to perform calculations with
the value. Also, when we plan to use a jQuery object more than once, it's generally
a good idea to cache the selector by storing the resulting jQuery object in a variable
as well.
$(document).ready(function() {
var $speech = $('div.speech');
$('#switcher-large').click(function() {
var num = parseFloat($speech.css('fontSize'), 10);
});
});
The first line inside $(document).ready() now stores a variable for <div
class="speech"> itself. Notice the use of a $ in the variable name, $speech. Since
$ is a legal character in JavaScript variables, we can use it as a reminder that the
variable is storing a jQuery object.
Inside the .click() handler, we use parseFloat() to get the font size property's
number only. The parseFloat() function looks at a string from left to right until
it encounters a non-numeric character. The string of digits is converted into a
fl oating-point (decimal) number. For example, it would convert the string '12' to
the number 12. In addition, it strips non-numeric trailing characters from the string,
so '12px' becomes 12 as well. If the string begins with a non-numeric character,
parseFloat() returns NaN, which stands for Not a Number. The second argument
for parseFloat() allows us to ensure that the number is interpreted as base-10
instead of octal or some other representation.
All that's left to do, if we are increasing by 40%, is to multiply num by 1.4 and then
set the font size by concatenating num and 'px':
$(document).ready(function() {
var $speech = $('div.speech');
$('#switcher-large').click(function() {
var num = parseFloat($speech.css('fontSize'), 10 );
num *= 1.4;
$speech.css('fontSize', num + 'px');
});
});
The equation num *= 1.4 is shorthand for num = num * 1.4. We can use
the same type of shorthand for the other basic mathematical operations,
as well: addition, num += 1.4; subtraction, num -= 1.4; division,
num /= 1.4; and modulus (division remainder), num %= 1.4.
Now when a user clicks on the Bigger button, the text becomes larger. Another click,
and the text becomes larger still, as shown in the following screenshot:
To get the Smaller button to decrease the font size, we will divide rather than
multiply—num /= 1.4. Better still, we'll combine the two into a single .click()
handler on all <button> elements within <div id="switcher">. Then, after finding
the numeric value, we can either multiply or divide depending on the ID of the
button that was clicked. Here is what that code looks like now:
$(document).ready(function() {
var $speech = $('div.speech');
$('#switcher button').click(function() {
var num = parseFloat( $speech.css('fontSize'), 10 );
if (this.id == 'switcher-large') {
num *= 1.4;
} else if (this.id == 'switcher-small') {
num /= 1.4;
}
$speech.css('fontSize', num + 'px);
});
});
Recall from Chapter 3 that we can access the id property of the DOM element
referred to by this, which appears here inside the if and else if statements.
Here, it is more efficient to use this than to create a jQuery object just to test the
value of a property.
It's also nice to have a way to return the font size to its initial value. To allow the
user to do so, we can simply store the font size in a variable immediately when the
DOM is ready. We can then use this value whenever the Default button is clicked.
To handle this click, we could add another else if statement. However, perhaps
a switch statement would be more appropriate.
$(document).ready(function() {
var $speech = $('div.speech');
var defaultSize = $speech.css('fontSize');
$('#switcher button').click(function() {
var num = parseFloat( $speech.css('fontSize'), 10 );
switch (this.id) {
case 'switcher-large':
num *= 1.4;
break;
case 'switcher-small':
num /= 1.4;
break;
default:
num = parseFloat(defaultSize, 10);
}
$speech.css('fontSize', num + 'px');
});
});
Here we're still checking the value of this.id and changing the font size based on it,
but if its value is neither 'switcher-large' nor 'switcher-small' it will default to
the initial font size.
Basic hide and show
The basic .hide() and .show() methods, without any parameters, can be thought
of as smart shorthand methods for .css('display','string'), where 'string' is
the appropriate display value. The effect, as might be expected, is that the matched
set of elements will be immediately hidden or shown, with no animation.
The .hide() method sets the inline style attribute of the matched set of elements
to display:none. The smart part here is that it remembers the value of the display
property—typically block or inline—before it was changed to none. Conversely,
the .show() method restores the matched set of elements to whatever visible display
property they had before display:none was applied.
For more information about the display property and how its values are
visually represented in a web page, visit the Mozilla Developer Center at
https://developer.mozilla.org/en/CSS/display/ and view
examples at https://developer.mozilla.org/samples/cssref/
display.html.
This feature of .show() and .hide() is especially helpful when hiding elements
whose default display property is overridden in a stylesheet. For example, the <li>
element has the property display:block by default, but we might want to change it
to display:inline for a horizontal menu. Fortunately, using the .show() method
on a hidden element such as one of these <li> tags would not merely reset it to its
default display:block, because that would put the <li> on its own line. Instead,
the element is restored to its previous display:inline state, thus preserving the
horizontal design.
A quick demonstration of these two methods can be set up by adding a second
paragraph and a "read more" link after the first paragraph in the example HTML:
<div id="switcher">
<div class="label">Text Size</div>
<button id="switcher-default">Default</button>
<button id="switcher-large">Bigger</button>
<button id="switcher-small">Smaller</button>
</div>
<div class="speech">
<p>Fourscore and seven years ago our fathers brought forth
on this continent a new nation, conceived in liberty,
and dedicated to the proposition that all men are
created equal.
</p>
<p>Now we are engaged in a great civil war, testing whether
that nation, or any nation so conceived and so dedicated,
can long endure. We are met on a great battlefield of
that war. We have come to dedicate a portion of that
field as a final resting-place for those who here gave
their lives that the nation might live. It is altogether
fitting and proper that we should do this. But, in a
larger sense, we cannot dedicate, we cannot consecrate,
we cannot hallow, this ground.
</p>
<a href="#" class="more">read more</a>
</div>
When the DOM is ready, the second paragraph is hidden:
$(document).ready(function() {
$('p:eq(1)').hide();
});
And the speech looks like the following screenshot:
Then, when the user clicks on read more at the end of the first paragraph, that link is
hidden and the second paragraph is shown:
$(document).ready(function() {
$('p:eq(1)').hide();
$('a.more').click(function() {
$('p:eq(1)').show();
$(this).hide();
return false;
});
});
Note the use of return false to keep the link from activating its default action.
Now the speech looks like this:
The .hide() and .show() methods are quick and useful, but they aren't very flashy.
To add some flair, we can give them a speed.
Effects and speed
When we include a speed (or, more precisely, a duration) with .show() or
.hide(), it becomes animated—occurring over a specified period of time. The
.hide('speed') method, for example, decreases an element's height, width,
and opacity simultaneously until all three reach zero, at which point the CSS rule
display:none is applied. The .show('speed') method will increase the element's
height from top to bottom, width from left to right, and opacity from 0 to 1 until its
contents are completely visible.
Speeding in
With any jQuery effect, we can use one of three preset speeds: 'slow', 'normal',
and 'fast'. Using .show('slow') makes the show effect complete in .6 seconds,
.show('normal') in .4 seconds, and .show('fast') in .2 seconds. For even greater
precision we can specify a number of milliseconds, for example .show(850). Unlike
the speed names, the numbers are not wrapped in quotation marks.
Let 's include a speed in our example when showing the second paragraph of
Lincoln's Gettysburg Address:
$(document).ready(function() {
$('p:eq(1)').hide();
$('a.more').click(function() {
$('p:eq(1)').show('slow');
$(this).hide();
return false;
});
});
When we capture the paragraph's appearance at roughly halfway through the effect,
we see something like the following:
Fading in and fading out
While the animated .show() and .hide() methods are certainly flashy, they
may at times be too much of a good thing. Fortunately, jQuery offers a couple
other pre-built animations for a more subtle effect. For example, to have the
whole paragraph appear just by gradually increasing the opacity, we can use
.fadeIn('slow') instead:
$(do cument).ready(function() {
$('p:eq(1)').hide();
$('a.more').click(function() {
$('p:eq(1)').fadeIn('slow');
$(this).hide();
return false;
});
});
This time when we capture the paragraph's appearance halfway, it's seen as:
The difference here is that the .fadeIn() effect starts by setting the dimensions of
the paragraph so that the contents can simply fade into it. To gradually decrease the
opacity we can use .fadeOut().
Compound effects
Sometimes we have a need to toggle the visibility of elements, rather than displaying
them once as we did in the previous example. Toggling can be achieved by first
checking the visibility of the matched elements and then attaching the appropriate
method. Using the fade effects again, we can modify the example script to look
like this:
$(document).ready(function() {
var $firstPara = $('p:eq(1)');
$firstPara.hide();
$('a.more').click(function() {
if ($firstPara.is(':hidden')) {
$firstPara.fadeIn('slow');
$(this).text('read less');
} else {
$firstPara.fadeOut('slow');
$(this).text('read more');
}
return false;
});
});
As we did earlier in the chapter, we're caching our selector here to avoid repeated
DOM traversal. Notice, too, that we're no longer hiding the clicked link; instead,
we're changing the its text.
Using an if else statement is a perfectly reasonable way to toggle elements'
visibility. But with jQuery's compound effects we can leave the conditionals
out of it (although, in this example, we still need one for the link text). jQuery
provides a .toggle() method, which acts like .show() and .hide(), and like
them, can be used with a speed argument or without. The other compound method
is .slideToggle(), which shows or hides elements by gradually increasing
or decreasing their height. Here is what the script looks like when we use the
.slideToggle() method:
$(document).ready(function() {
var $firstPara = $('p:eq(1)');
$firstPara.hide();
$('a.more').click(function() {
$firstPara.slideToggle('slow');
var $link = $(this);
if ( $link.text() == "read more" ) {
$link.text('read less');
} else {
$link.text('read more');
}
return false;
});
});
This time $(this) would have been repeated, so we're storing it in the $link
variable for performance and readability. Also, the conditional statement checks for
the text of the link rather than the visibility of the second paragraph, since we're only
using it to change the text.
Creating custom animations
In addition to the pre-built effect methods, jQuery provides a powerful
.animate() method that allows us to create our own custom animations with
fine-grained control. The .animate() method comes in two forms. The first takes
up to four arguments:
- A map of style properties and values—similar to the .css() map discussed
earlier in this chapter
- An optional speed—which can be one of the preset strings or a number
of milliseconds
- An optional easing type—an advanced option discussed in Chapter 10
- An optional callback function—which will be discussed later in this chapter
All together, the four arguments look like this:
.animate({property1: 'value1', property2: 'value2'},
speed, easing, function() {
alert('The animation is finished.');
}
);
The second form takes two arguments, a map of properties and a map of options.
.animate({properties}, {options})
In effect, the second argument wraps up the second through fourth arguments of the
first form into another map, and adds two more options to the mix. When we adjust
the line breaks for readability, the second form looks like this:
.animate({
property1: 'value1',
property2: 'value2'
}, {
duration: 'value',
easing: 'value',
complete: function() {
alert('The animation is finished.');
},
queue: boolean,
step: callback
});
For now we'll use the first form of the .animate() method, but we'll return to the
second form later in the chapter when we discuss queuing effects.
Toggling the fade
When we discussed compound effects, did you notice that not all methods have a
corresponding method for toggling? That's right: while the sliding methods include
.slideToggle(), there is no corresponding .fadeToggle() to go along with
.fadeIn() and .fadeOut()! The good news is that we can use the .animate()
method to easily make our own toggling fade animation. Here, we'll replace the
.slideToggle() line of the previous example with our custom animation:
$(document).ready(function() {
$('p:eq(1)').hide();
$('a.more').click(function() {
$('p:eq(1)').animate({opacity: 'toggle'}, 'slow');
var $link = $(this);
if ( $link.text() == "read more" ) {
$link.text('read less');
} else {
$link.text('read more');
}
return false;
});
});
As the example illustrates, the .animate() method provides convenient shorthand
values for CSS properties — 'show', 'hide', and 'toggle' — to ease the way when
the shorthand methods aren't quite right for the particular task.
Animating multiple properties
With the .animate() method, we can modify any combination of properties
simultaneously. For example, to create a simultaneous sliding and fading effect when
toggling the second paragraph, we simply add the height property-value pair to
.animate()'s properties map:
$(document).ready(function() {
$('p:eq(1)').hide();
$('a.more').click(function() {
$('p:eq(1)').animate({
opacity: 'toggle',
height: 'toggle'
},
'slow');
var $link = $(this);
if ( $link.text() == "read more" ) {
$link.text('read less');
} else {
$link.text('read more');
}
return false;
});
});
Additionally, we have not only the style properties used for the shorthand effect
methods at our disposal, but also other properties such as: left, top, fontSize,
margin, padding, and borderWidth. Recall the script to change the text size of
the speech paragraphs. We can animate the increase or decrease in size by simply
substituting the .animate() method for the .css() method:
$(document).ready(function() {
var $speech = $('div.speech');
var defaultSize = $speech.css('fontSize');
$('#switcher button').click(function() {
var num = parseFloat( $speech.css('fontSize'), 10 );
switch (this.id) {
case 'switcher-large':
num *= 1.4;
break;
case 'switcher-small':
num /= 1.4;
break;
default:
num = parseFloat(defaultSize, 10);
}
$speech.animate({fontSize: num + 'px'},
'slow');
});
});
The extra properties allow us to create much more complex effects, too. We can, for
example, move an item from the left side of the page to the right while increasing its
height by 20 pixels and changing its border width to 5 pixels.
So, let's do that with the <div id="switcher"> box. Here is what it looks like before
we animate it:
With a fl exible-width layout, we need to compute the distance that the box needs to
travel before it lines up at the right side of the page. Assuming that the paragraph's
width is 100%, we can subtract the Text Size box's width from the paragraph's
width. While jQuery's .width() method would usually come in handy for such
calculations, it doesn't factor in the width of the right and left padding or the right
and left border. As of jQuery version 1.2.6, though we also have the .outerWidth()
method at our disposal. This is what we'll use here, to avoid having to add padding
and border widths as well. For the sake of this example, we'll trigger the animation
by clicking the Text Size label, just above the buttons. Here is what the code should
look like:
$(document).ready(function() {
$('div.label').click(function() {
var paraWidth = $('div.speech p').outerWidth();
var $switcher = $(this).parent();
var switcherWidth = $switcher.outerWidth();
$switcher.animate({left: paraWidth - switcherWidth,
height: '+=20px', borderWidth: '5px'}, 'slow');
});
});
Note that the height property has += before the pixel value. This expression,
introduced in jQuery 1.2, indicates a relative value. So, instead of animating the
height to 20 pixels, the height is animated to 20 pixels greater than the current height.
Although this code successfully increases the height of the <div> and widens its
border, at the moment the left position cannot be changed. We still need to enable
changing its position in the CSS.
Positioning with CSS
When working with .animate(), it's important to keep in mind the limitations that
CSS imposes on the elements that we wish to change. For example, adjusting the
left property will have no effect on the matching elements unless those elements
have their CSS position set to relative or absolute. The default CSS position for all
block-level elements is static, which accurately describes how those elements will
remain if we try to move them without first changing their position value.
For more information on absolute and relative positioning, see Joe
Gillespie's article, Absolutely Relative at: http://www.wpdfd.com/
issues/78/absolutely_relative/
A peek at our stylesheet shows that we have now set <div id="switcher"> to be
relatively positioned:
#switcher {
position: relative;
}
With the CSS taken into account, the result of clicking on Text Size, when the
animation has completed, will look like this:
|