Chapter 3
In This Chapter
Hiding and showing elements with jQuery
Fading elements in and out
Adding a callback function to a transition
Element animation
Object chaining
Using selection filters
Adding and removing elements
The jQuery library simplifies a lot of JavaScript coding. One of its best features is how it adds features that would be difficult to achieve in ordinary JavaScript and DOM programming. This chapter teaches you to shake and bake your programs by identifying specific objects, moving them around, and making them appear, slide, and fade.
To get it all started, take a look at hideShow.html shown in Figure 3-1.
The hideShow program looks simple at first, but it does some quite interesting things. All of the level-two headings are actually buttons, so when you click them, interesting things happen:
You can adjust how quickly the transition animation plays. You can specify exactly how long the transition takes in milliseconds (1/1000 of a second). Also, any transition can have a callback function attached.
The animations shown in this example are useful when you want to selectively hide and display parts of your page:
The jQuery library has built-in support for transitions that make these effects pretty easy to produce. Look over the entire program before digging into the details:
<!DOCTYPE html>
<html lang = "en-US">
<head>
<title>hideShow.html</title>
<meta charset = "UTF-8">
<style type = "text/css">
#content {
width: 400px;
height: 200px;
font-size: 200%;
padding-left:1em;
background-color: yellow;
position: absolute;
left: 300px;
top: 100px;
}
h2 {
width: 10em;
border: 3px outset black;
background-color: lightgray;
text-align: center;
font-family: sans-serif;
border-radius: 5px;
box-shadow: 5px 5px 5px gray;
}
</style>
<script type = "text/javascript"
src = "jquery-1.10.2.min.js"></script>
<script type = "text/javascript">
$(init);
function init(){
//styleContent();
$("#content").hide();
$("#show").click(showContent);
$("#hide").click(hideContent);
$("#toggle").click(toggleContent);
$("#slideDown").click(slideDown);
$("#slideUp").click(slideUp);
$("#fadeIn").click(fadeIn);
$("#fadeOut").click(fadeOut);
} // end init
function showContent(){
$("#content").show();
} // end showContent
function hideContent(){
$("#content").hide();
} // end hideContent
function toggleContent(){
$("#content").toggle();
} // end toggleContent
function slideDown(){
$("#content").slideDown("medium");
} // end slideDown
function slideUp(){
$("#content").slideUp(500);
} // end slideUp
function fadeIn(){
$("#content").fadeIn("slow", present);
} // end fadeIn
function fadeOut(){
$("#content").fadeOut("fast");
} // end fadeOut.
function present(){
alert("I'm here");
} // end present
</script>
</head>
<body>
<h1>Hide and show</h1>
<h2 id = "show">Show</h2>
<h2 id = "hide">Hide</h2>
<h2 id = "toggle">Toggle</h2>
<h2 id = "slideDown">Slide Down</h2>
<h2 id = "slideUp">Slide Up</h2>
<h2 id = "fadeIn">Fade In</h2>
<h2 id = "fadeOut">Fade Out</h2>
<p id = "content">
This is the content. It is hidden at first, but it is hidden and
shown with jQuery techniques.
</p>
</body>
</html>
This example may look long and complicated when you view it all at once, but it really isn't hard to understand when you break it into pieces.
The HTML used in this example is minimal, as is common in jQuery development:
The level-two headings will be used as buttons in this example. I use a CSS style to make the H2 tags look more like buttons (adding a border and background color). I added an ID attribute to every button so that I can add jQuery events later.
The other interesting part of the HTML is the content div. In this example, the actual content isn't really important, but I did add some CSS to make the content easy to see when it pops up.
The initialization sequence simply sets the stage and assigns a series of event handlers:
$(init);
function init(){
//styleContent();
$("#content").hide();
$("#show").click(showContent);
$("#hide").click(hideContent);
$("#toggle").click(toggleContent);
$("#slideDown").click(slideDown);
$("#slideUp").click(slideUp);
$("#fadeIn").click(fadeIn);
$("#fadeOut").click(fadeOut);
} // end init
Use the $(document).ready() mechanism (described in Chapter 2 of this minibook) or this cleaner shortcut to specify an initialization function.
When the user first encounters the page, the content div should be hidden.
This program is a series of small functions. The init() function attaches each function to the corresponding button. Note how I carefully named the functions and buttons to make all the connections easy to understand.
All the effects on this page are based on hiding and showing the content div. The hide() and show() methods illustrate how jQuery animation works:
function showContent(){
$("#content").show();
} // end showContent
function hideContent(){
$("#content").hide();
} // end hideContent
Each of these functions works in the same basic manner:
The hide and show methods act instantly. If the element is currently visible, the show() method has no effect. Likewise, hide() has no effect on an element that's already hidden.
In addition to hide() and show(), the jQuery object supports a toggle() method. This method takes a look at the current status of the element and changes it. If the element is currently hidden, it becomes visible. If it's currently visible, it is hidden. The toggleContent() function illustrates how to use this method:
function toggleContent(){
$("#content").toggle();
} // end toggleContent
jQuery supports effects that allow you to animate the appearance and disappearance of your element. The general approach is very similar to hide() and show(), but you find one additional twist:
function slideDown(){
$("#content").slideDown("medium");
} // end slideDown
function slideUp(){
$("#content").slideUp(500);
} // end slideUp
The slideDown() method makes an element appear like a window shade being pulled down. The slideUp() method makes an element disappear in a similar manner.
These functions take a speed parameter that indicates how quickly the animation occurs. If you omit the speed parameter, the default value is medium. The speed can be these string values:
A slideToggle() function is also available that toggles the visibility of the element, but using the sliding animation technique.
A third type of “now you see it” animation is provided by the fade methods. These techniques adjust the opacity of the element. The code should look quite familiar by now:
function fadeIn(){
$("#content").fadeIn("slow", present);
} // end fadeIn
function fadeOut(){
$("#content").fadeOut("fast");
} // end fadeOut.
function present(){
alert("I'm here");
} // end present
fadeIn() and fadeout() work just like the hide() and slide() techniques. The fading techniques adjust the opacity of the element and then remove it, rather than dynamically changing the size of the element as the slide and show techniques do.
If the element is already visible, the callback method is triggered immediately.
The jQuery library also has interesting features for changing any of an element's characteristics, including its position. The animate.html page featured in Figure 3-3 illustrates a number of interesting animation techniques.
This page illustrates how to move a jQuery element by modifying its CSS. It also illustrates an important jQuery technique called object chaining and a very useful animation method that allows you to create smooth motion over time. As usual, look over the entire code first; I break it into sections for more careful review.
<!DOCTYPE html>
<html lang = "en-US">
<head>
<title>Animate.html</title>
<meta charset="UTF-8">
<style type = "text/css">
#content {
width: 300px;
height: 200px;
font-size: 200%;
background-color: yellow;
position: absolute;
left: 300px;
top: 100px;
padding-left: .5em;
}
</style>
<script type = "text/javascript"
src = "jquery-1.10.2.min.js"></script>
<script type = "text/javascript">
$(init);
function init(){
$("#move").click(move2);
$("#glide").click(glide);
$("#left").click(left);
$("#right").click(right);
} // end init
function move2(){
$("#content").css("left", "50px");
$("#content").css("top", "100px");
} // end move2
function move(){
$("#content").css("left", "50px")
.css("top", "100px");
} // end move
function glide(){
//move to initial spot
$("#content").css("left", "50px")
.css("top", "100px");
//slide to new spot
$("#content").animate({
"left": "400px",
"top": "200px"
}, 2000);
} // end glide
function left(){
$("#content").animate({"left": "-=10px"}, 100);
} // end left
function right(){
$("#content").animate({"left": "+=10px"}, 100);
} // end left
</script>
</head>
<body>
<h1>Animation Demo</h1>
<form action = "">
<fieldset>
<button type = "button"
id = "move">
move
</button>
<button type = "button"
id = "glide">
glide
</button>
<button type = "button"
id = "left">
<--
</button>
<button type = "button"
id = "right">
-->
</button>
</fieldset>
</form>
<p id = "content">
This content will move in response to the controls.
</p>
</body>
</html>
The HTML always forms the foundation. This page is similar to the hideShow page, but I decided to use a real form with buttons as the control panel. Buttons are not difficult to use, but they are a little more tedious to code because they must be inside a form element as well as a block-level element, and they require more coding to produce than H2 elements.
Note that I used < in one of the button captions. This HTML attribute displays the less-than symbol. Had I used the actual symbol (<), the browser would have thought I was beginning a new HTML tag and would have been confused.
The buttons all have id attributes, but I didn't attach functions to them with the onclick attribute. After you're using jQuery, it makes sense to commit to a jQuery approach and use the jQuery event techniques.
The initialization is all about setting up the event handlers for the various buttons. An init() function is called when the document is ready. That function contains function pointers for the various events, directing traffic to the right functions when a button is pressed:
function init(){
$("#move").click(move);
$("#glide").click(glide);
$("#left").click(left);
$("#right").click(right);
} // end init
As usual, naming conventions makes it easy to see what's going on.
The move() function isn't really that radical. All it does is use the css() method described in Book VII, Chapter 2 to alter the position of the element. After all, position is just a CSS attribute, right? Well, it's a little more complex than that.
Your first attempt at a move() function would probably look like this:
function move(){
$("#content").css("left", "50px");
$("#content").css("top", "100px");
} // end move
Almost all jQuery methods return a jQuery object as a side effect. So, the line
$("#content").text("changed");
not only changes the text of the content node but also makes a new node. You can attach that node to a variable like this if you want:
var newNode = $("#content").text("changed");
However, what most jQuery programmers do is simply attach new functionality onto the end of the previously defined node, like this:
$("#content").text("changed").click(hiThere);
This new line takes the node created by $(“#content”) and changes its text value. It then takes this new node (the one with changed text) and adds a click event to it, calling the hiThere() function when the content element is clicked. In this way, you build an ever-more complex node by chaining nodes on top of each other.
$("#content")
.text("changed")
.click(hiThere);
Note that only the last line has a semicolon because it's all one line of logic even though it occurs on three lines in the editor.
Object chaining makes it easy to build the move() function so that it shifts the content's left and top properties simultaneously:
function move(){
$("#content").css("left", "50px")
.css("top", "100px");
} // end move
This function uses the css() method to change the left property to 50px. The resulting object is given a second css() method call to change the top property to 100px. The top and left elements are changed at the same time as far as the user is concerned.
Using the css() method is a great way to move an element around on the screen, but the motion is instantaneous. jQuery supports a powerful method called animate() that allows you to change any DOM characteristics over a specified span of time. The glide button on animate.html smoothly moves the content div from (50, 100) to (400, 200) over two seconds:
function glide(){
//move to initial spot
$("#content").css("left", "50px")
.css("top", "100px");
//slide to new spot
$("#content").animate({
"left": "400px",
"top": "200px"
}, 2000);
} // end glide
The function begins by moving the element immediately to its initial spot with chained css() methods. It then uses the animate() method to control the animation. This method can have up to three parameters:
You can also use the animation mechanism to move an object relative to its current position. The arrow buttons and their associated functions perform this task:
function left(){
$("#content").animate({"left": "-=10px"}, 100);
} // end left
function right(){
$("#content").animate({"left": "+=10px"}, 100);
} // end left
These functions also use the animate() method, but you see a small difference in the position parameters. The += and –= modifiers indicate that I want to add to or subtract from (respectively) the value rather than indicating an absolute position. Of course, you can add as many parameters to the JSON object as you want, but these are a good start.
Note that because I'm moving a small amount (10 pixels), I want the motion to be relatively quick. Each motion lasts 100 milliseconds, or 1/10 of a second.
The jQuery library supports a third major way of modifying the page: the ability to add and remove contents dynamically. This is a powerful way to work with a page. The key to this feature is another of jQuery's most capable tools — its flexible selection engine. You can also use numerous attributes to modify nodes. The changeContent.html page, shown in Figure 3-4, demonstrates some of the power of these tools.
Of course, the buttons allow the user to make changes to the page dynamically. Clicking the Add Text button adds more text to the content area, as you can see in Figure 3-5.
The code for changeDocument.html seems complex, but it follows the same general patterns you've seen in jQuery programming. As always, look over the entire code first and then read how it breaks down:
<!DOCTYPE html>
<html lang = "en-US">
<head>
<title>changeContent.html</title>
<meta charset = "UTF-8">
<style type = "text/css">
#content {
width: 300px;
background-color: yellow;
left: 300px;
top: 100px;
padding-left: .5em;
border: 0px none black;
}
div {
border: 3px solid red;
padding: 2px;
}
</style>
<script type = "text/javascript"
src = "jquery-1.10.2.min.js"></script>
<script type = "text/javascript">
$(init);
function init(){
$("#reset").click(reset);
$("#addText").click(addText);
$("#wrap").click(wrap);
$("#clone").click(clone);
$("#oddGreen").click(oddGreen);
} // end init
function reset(){
//remove all but the original content
$("p:gt(0)").remove();
$("div:not(#content)").remove();
//reset the text of the original content
$("#content").html("<p>This is the original content</p>");
} // end reset
function addText(){
$("p:first").append(" …and this was added later.");
} // end addContent
function wrap(){
$("p:gt(0)").wrap("<div></div>");
} // end wrap
function clone(){
$("p:first").clone()
.insertAfter("p:last")
.css("backgroundColor", "lightblue");
} // end clone
function oddGreen(){
//turn alternate (odd numbered) paragraph elements green
$("p:odd").css("backgroundColor", "green")
.css("color", "white");
} // end oddGreen
</script>
</head>
<body>
<h1>Adding Content Demo</h1>
<form action = "">
<fieldset>
<button type = "button"
id = "reset">
reset
</button>
<button type = "button"
id = "addText">
add text
</button>
<button type = "button"
id = "clone">
clone
</button>
<button type = "button"
id = "wrap">
wrap in div
</button>
<button type = "button"
id = "oddGreen">
change alternate paragraphs
</button>
</fieldset>
</form>
<div id = "content">
<p>
This is the original content
</p>
</div>
</body>
</html>
Admittedly you see a lot of code here, but when you consider how much functionality this page has, it really isn't too bad. Look at it in smaller pieces, and it all makes sense.
As usual, begin by inspecting the HTML. The basic code for this page sets up the playground:
This form will become the control panel. Add a button for each function you want to add. Make sure that each button has an ID, but you don't need to specify an onclick() function because the init() function takes care of that.
Build a div called content, and add a paragraph to the div.
The initialization section is pretty straightforward. Set up an init() function, and use it to assign event handlers to all the buttons:
$(init);
function init(){
$("#reset").click(reset);
$("#addText").click(addText);
$("#wrap").click(wrap);
$("#clone").click(clone);
$("#oddGreen").click(oddGreen);
} // end init
It's pretty easy to add text to a component. The append() method attaches text to the end of a jQuery node. Table 3-1 shows a number of other methods for adding text to a node.
Table 3-1 Methods That Add Text to a Node
Method |
Description |
append(text) |
Adds the text (or HTML) to the end of the selected element(s) |
prepend(text) |
Adds the content at the beginning of the selected element(s) |
insertAfter(text) |
Adds the text after the selected element (outside the element) |
insertBefore(text) |
Adds the text before the selected element (outside the element) |
function addText(){
$("p:first").append(" …and this was added later.");
} // end addContent
The append() method adds text to the end of the element, but inside the element (rather than after the end of the element). In this example, the text will become part of the paragraph contained inside the content div. The more interesting part of this code is the selector. It could read like this:
$("p").append(" …and this was added later.");
That would add the text to the end of the paragraph. The default text has only one paragraph, so that makes lots of sense. If there are more paragraphs (and there will be), the p selector can select them all, adding the text to all the paragraphs simultaneously. By specifying p:first, I'm using a special filter to determine exactly which paragraph should be affected.
Many of the examples on this page use jQuery filters, so I describe them elsewhere in the following sections. For now, note that p:first means the first paragraph. Of course, you also see p:last and many more. Read on. . . .
You can clone (copy) anything you can identify as a jQuery node. This makes a copy of the node without changing the original. The cloned node isn't immediately visible on the screen. You need to place it somewhere, usually with an append(), prepend(), insertBefore(), or insertAfter() method.
Take a look at the clone() function to see how it works:
function clone(){
$("p:first").clone()
.insertAfter("p:last")
.css("backgroundColor", "lightblue");
} // end clone
The first paragraph is the one I want to copy. (In the beginning, only one exists, but that will change soon.)
Now you've made a copy, but it still isn't visible. Use chaining to do some interesting things to this copy.
The p:last identifier is the last paragraph, so insertAfter(“p:last”) means put the new paragraph after the last paragraph available in the document.
Just for grins, chain the css() method onto the new element and change the background color to light blue. This just reinforces the fact that you can continue adding commands to a node through chaining.
Note that the paragraphs are inside content. Of course, I could have put them elsewhere with careful use of selectors, but I put them where I want them.
It's hard to keep track of changes to the page because a standard view source command shows you the original source code, not the code that's been changed by your jQuery magic. jQuery changes the HTML of your page in memory but doesn't change the text file that contains your page. If your page is not doing what you expect, you need to look at the script-generated source code to see what's really going on.
Note that the content of the first paragraph is cloned with its current content and style information copied to the new element. If you clone the paragraph and then add content to it and clone it again, the first clone has the default text and the second clone will contain the additional text. If you modify the CSS style of an element and then clone it, the clone also inherits any of the style characteristics of the original node.
Sometimes you want to embed an object inside another element (or two). For example, the wrap button on the changeContent page surrounds each cloned paragraph with a <div></div> pair. I've defined the div tag in my CSS to include a red border. Repeatedly clicking the wrap button surrounds all cloned paragraphs with red borders. This would be a very tedious effect to achieve in ordinary DOM and JavaScript, but jQuery makes it pretty easy to do:
function wrap(){
$("p:gt(0)").wrap("<div></div>");
} // end wrap
The wrap() method is pretty easy to understand. If you feed it any container tag, it wraps that container around the selected node. You can also use multiple elements, so if you wanted to enclose a paragraph in a single item list, you could do something like this:
$("p").wrap("<ul><li></li></ul>");
The resulting code would surround each paragraph with an unordered list and list item.
Returning to the wrap() function, I've decided not to wrap every paragraph with a div, just the ones that have been cloned. (Mainly I'm doing this so that I can show you some other cool selection filters.) The p:gt(0) selector means to select all paragraphs with an index greater than 0. In other words, ignore the first paragraph, but apply the following methods to all other paragraphs. You also find these filters:
It's a common effect to alternate background colors on long lists or tables of data, but this can be a tedious effect to achieve in ordinary CSS and JavaScript. Not surprisingly, jQuery selectors make this a pretty easy job:
function oddGreen(){
//turn alternate (odd numbered) paragraph elements green
$("p:odd").css("backgroundColor", "green")
.css("color", "white");
} // end oddGreen
The :odd selector only chooses elements with an odd index and returns a jQuery node that can be further manipulated with chaining. Of course, you also see an :even selector for handling the even-numbered nodes. The rest of this code is simply CSS styling.
You need to be able to restore the page to its pristine state. A quick jQuery function can easily do the trick:
function reset(){
//remove all but the original content
$("p:gt(0)").remove();
$("div:not(#content)").remove();
//reset the text of the original content
$("#content").html("<p>This is the original content</p>");
} // end reset
This function reviews many of the jQuery and selection tricks shown in this chapter:
Any paragraph with an index greater than 0 is a clone, so it needs to go away. The remove() method removes all jQuery nodes associated with the current selector.
I could have used the :gt selector again, but instead I use another interesting selector — :not. This removes every div that isn't the primary content div. This removes all divs added through the wrap() function.
Set the default text back to its original status so that the page is reset.
The jQuery selectors and filters are really fun and powerful. Table 3-2 describes a few more filters and indicates how they might be used.
Table 3-2 Selected jQuery Filters
Filter |
Description |
:header |
Any header tag (H1, H2, H3). |
:animated |
Any element that is currently being animated. |
:contains(text) |
Any element that contains the indicated text. |
:empty |
The element is empty. |
:parent |
This element contains some other element. |
:attribute=value |
The element has an attribute with the specified value. |