CS 120, Fall 2011, Lab 10:
JQuery Animations

The goal of this lab is to start using JQuery, and in particular JQuery events and JQuery animations. You will use JQuery to program some of the animation effects that were demonstrated in class on Wednesday.

To begin the lab, create a new Aptana project named lab10. Open the directory /classes/cs120/lab10-files and copy the contents of that directory into your lab10 project.

This lab is due by the beginning of lab next Friday.

About JQuery

This section reviews some information about JQuery that you will need for the lab. The actual action of the lab starts in the next section. JQuery is a JavaScript library; that is, it defines some functions and utilities that you can use. To use JQuery on a web page, it has to be imported into that page with a script element such as

<script type="text/javascript" src="jquery-1.6.4.min.js"></script>

The version number, 1.6.4, will depend on what version of JQuery you are using. This tag assumes that the jquery file is in the same directory as the web page. It is also possible to use a relative URL to the file in a different directory, and this can be more efficient if you are using JQuery in a number of pages throughout your site.

To use JQuery, you want to write another <script> on the page. Often, this script will use an initialization function, function init(), that contains code that needs to be executed after the complete page is ready. Typically, you would use this method to add event-handling functions to elements on the page and to adjust CSS properties or other attributes of elements. To make this function actually execute at the appropriate time, the script would also include the command $(init);, which is a funny JQuery idiom for saying "execute the function init when the complete document is ready." The script will also contain definitions of event-handling functions. For example, here is a complete script that will make any paragraph on the page move 25 pixels to the right when the paragraph is clicked with the mouse:

<script type="text/javascript">
   function init() {
      $("p").css("position","relative");
      $("p").click(jumpright);
   }
   $(init);
   
   function jumpright() { // event handling function.
      $(this).animate( { left: 25 } );  // "this" will be explained below
   }
</script>

This script has actually been applied to this very page, so you can try it out!

Here is a table of some of the commands that you can use in your scripts when JQuery has been imported into the page. Note that the first three commands can also be used as functions, by omitting the last parameter. For example, $("#img1").attr("src") is a function call that returns the current value of the "src" attribute of the element with id="img1". And $("#div3").css("display") returns the current value of the CSS display property for the element with id="#div3". Remember that the select in all these functions can be just about any CSS selector; this is one of the most powerful ideas in JQuery.

General Form Examples and Notes
$(select).attr(name,value); $("#img1").attr("src","pic2.jpg"); -- set the source URL of an image.
$("button").attr("disabled",true); -- disable all buttons.
$(select).html(text); $("#message").html("Error"); -- sets content of element to Error; this is similar to document.getElementById("message").innerHTML = "Error";
$(select).css(property,value); $("#div3").css("display","none"); -- hides the element with id="div3".
$(select).click(function); $("p.clickable").click(doClickOnPar); -- installs event handler for mouse click; function doClickOnPar is called when user clicks any <p> with class="clickable"
$(select).mouseover(function); $("#btn1").click(mouseOverButton); -- installs mouseover event handler on element with id="btn1"; function mouseOverButton is called when user moves mouse over it
$(select).fadeOut(time,nextFunction)
[parameters are optional]
$("#div3").fadeOut(1000,phase2); -- hide element with id="div3" by fading it to nothing over a period of 1000 milliseconds then call the function phase2.
$(select).fadeIn(time,nextFunction)
[parameters are optional]
$("#div3").fadeIn(1000,phase2); -- show element with id="div3" by fading it in over a period of 1000 milliseconds then call the function phase2.
$(select).slideUp(time,nextFunction)
[parameters are optional]
$("#div3").slideUp(1000,phase2); -- hide element with id="div3" by sliding it up over a period of 1000 milliseconds then call the function phase2.
$(select).slideDown(time,nextFunction)
[parameters are optional]
$("#div3").slideDown(1000,phase2); -- show element with id="div3" by sliding it down over a period of 1000 milliseconds then call the function phase2.
$(select).animate(properties,time,next);
[time and next are optional]
$("#img1").animate( { width: 100, height: 75 }, 1000 ); -- change the width and height of element with id="#img1" from their current values to 100,75 over a period of 100 milliseconds

Animated Coin Flip

In Lab 6, you wrote a short JavaScript program for flipping a coin. In this exercise, you will create an animated version using JQuery. Open the file jquery-flip.html for editing. Note that it already imports the jquery library (as do all the html files for this lab). You will have to add another script, after the line that imports the jquery library.

In your script, you should write the function that will be called when the "Flip It!" button is pressed. You must also arrange for that function to be called when the button is clicked. You can do this the old way (using <button onclick=...), or the new JQuery way (using $(...).click(...) in an init function). In this case, I would say the old way is easier. If you have any doubts that you've done it right, put an alert in your function, and check that the alert appears when the button is clicked. If your JavaScript doesn't appear to be working, remember to check the Firefox or Chrome error console to see if any error has been reported. (Reload the page if necessary to see the most recent errors.)

Next, you have to write the event-handling function and implement the coin flip. The process takes place in several phases. The sequence of phases can be implemented by using the "nextFunction" parameter in JQuery's animation functions. Remember that that optional parameter can give the name of a function that is to be called when the animation is finished. The idea of specifying a function to be called at a later time is one of the most important ideas in this lab, and you should make sure that you understand it. Here are the three phases for a simple coin flip.

  1. The first function, which is called when the button is clicked, should start the animation that fades out the current image. The "Flip It!" button should be disabled while the coin is being flipped. You should disable it in this function.
  2. In the second-phase function, decide whether the flip is heads or tails, and set the src of the img element to be the url of the appropriate image file. Start the animation that fades in the image.
  3. The third phase is simply to re-enable the button

If you are more ambitious, you could add extra phases so that the current image will fade out, the question-mark image will fade in and then immediately fade out again, and then the image of the coin fades back in at the end. (You probably want to go on to the rest of the lab first, and come back to this part later.)

Jumpy Text

For the second exercise, you will make each of the words on a page jump out of the way when the mouse is moved over it. Open the file jumpy-text.html. This web page contains a headline and a paragraph. Each word on the page is in a <span> element with class="word", so you can easily write a CSS selector that selects all the words on the page. Note: I did not type in all the tags -- I used a program to surround each word with <span class="word"> and </span>. (The paragraph that is used here is some pseudo-latin that is often used for sample text. It is taken from the Wikipedia article on Lorem ipsum).

In this exercise, you will be animating the location of elements. Recall that for this to work, you need to set the CSS position property of the elements to "relative". Here, you want to set the CSS position of every <span> with class="word". You can do this in either of two ways: By adding a rule to the <style> section of the page, or by using JQuery's $(...).css function. Either way is fine. If you choose the JQuery way, do it in an init function.

You also have to install an event handler on every <span> element with class="word". The event handler is a function, and you want it to be called when the user moves the mouse over the element. The only way to install the handlers easily is to use JQuery's $(...).mouseover function. Again, you should do this in the init function.

Finally, you have to write the function that will be called when in response to mouseover event. As a first example, try using this function:

function mouseOverWord() {
      $(this).css("top","200px");
}

This will make the word jump to a location 200 pixels below its usual location when the mouse moves over it. (Try it!)

This example introduces for the first time in this course an important feature of JavaScript: this. Here is the issue: The same event-handling function is attached to many elements, in fact to each word on the page. The question is, when the mouse moves over a word and the function is called, how does the function know which word it was? It has to know that, since only that word is supposed to be moved. The answer is this; this is a special variable that tells the event-handler which particular element caused the event. When used in a JQuery event handler, you can use $(this) just like you would use $("#btn") or $("#div3"). In this case, calling $(this).css("top","200px") modifies the particular word that the mouse was over by setting the value of the "top" property to "200px".

But what we really want here is to run an animation that makes the text move away from the mouse and then back. To do that, you have to use $(...).animate. The first parameter to this function is an object that specifies the CSS properties that you want to animate and the values that they should have at the end of the animation. The values are specified in pixels (unless you give the value as a string with a different unit of measurement). To write the object, you can use object notation, with {...}. (It looks a lot like a CSS rule, but the items are separated with commas instead of semicolons.) Some examples of objects that might be used with $(...).animate:

{ top: 200 }

{ width: 300, height: 300 }

{ top: 0, left: 0 }

Try modifying the mouseOverWord function to use

$(this).animate( { top: 200 } );

and check out how it makes the page behave. The words end up in the same place as before, but now you can see them move.

To complete the exercise, you still have to do a few things. First, you have to animate both top and left. Second, after the words move to their new position, you want them to move back to the original position (with top and left both equal to zero). That means that you need to run another animation when the first animation completes. And third, instead of the new position being given by constant values, you want to use randomly generated values so that each word will pick a different amount to jump by. For the random values, you can use the following formula or something similar. This formula gives a random integer in the range -50 to 50:

Math.floor( 101 * Math.random() ) - 50

I encourage you to finish the exercise in a series of steps and try out the result at each step. You can get a variety of interesting effects!

Click to Show/Hide

For your third exercise, you should work on the file math_courses.html. That page contains a lot of hidden div's, along with a bunch of visible course names. You want to make it possible to show the hidden content by clicking on the course names. The information for a typical course looks like this:

<div class="coursename" id="m100">
<p class="coursename">100. Precalculus: Elementary Functions</p>
</div>
<div class="description" id="m100-description"> 
<p>Intended for students who plan to continue in the calculus
sequence, this course involves the study of basic functions:
polynomial, rational, exponential, logarithmic, and trigonometric.
Topics include a review of the real number system, equations and
inequalities, graphing techniques, and applications of functions.
Includes problem-solving laboratory sessions. Permission of instructor
is required. This course does not count toward the major or minor in
mathematics. (<i>Offered annually</i>)</p>
</div>

There are two div's, one holding the name, which is visible on the page, and one holding the course description, which is initially hidden. Note that the id for the description can be obtained from the id for the course name by adding "-description". In this example, the two ids are "m100" and "m100-description". This is very important since it will allow you to compute the second id from the first. You will need to do that in your script.

So, you should use $(...).click to add a click handler to every div that has class="coursename". (Remember: you can do this with one line in the init function.)

The function that you write can use a slideDown animation to reveal a div that was previously hidden. Since the function has to work with many different div's, you need to use $(this) again to refer to the particular div that was clicked. However, this refers to the div that was clicked, not to the one that you want to show! You have to figure out which div to show. Fortunately, there is a way to find its id.

You can find out the id of the div that was clicked by calling $(this).attr("id"). This will be something like "m130". If you add "-description" to that, you get the id of the div that you want to show. If you put "#" in front of that, you get something that you can use as the selector in $(...).slideDown. It will be a good idea to make a variable.

var select = "#" + $(this).attr("id") + "-description";

With that, it should be possible to make the hidden div appear when the user clicks the course name. However, I want you to do a little more: If the user clicks a course name and the corresponding description is already showing, you should hide the div with a slideUp animation. It's easy to find out whether the div is currently hidden or not by testing if $(select).css("display") == "none".

An Accordion List

Once you have the third exercise working, you can do the final exercise. For the fourth exercise, work on cs_courses.html. This exercise is very much like the previous one. In fact, you can begin by copying the script that you wrote from math_courses.html into cs_courses.html. Then you should modify the script to implement the so-called "accordion list" behavior.

In an accordion list, only one item should be visible at a time. To implement this, as you slide one element down, you should simultaneously slide another one up. It's easy to do simultaneous animations with JQuery. In fact, if you start two animations on two different elements, those animations will automatically run simultaneously. (If you apply two animations to the same element, however, the second animation will be run after the fist one completes.)

The only difficulty is knowing which element to slideUp as you slide a new element down. To implement that, you have to use a variable to keep track of which element is currently showing. You can, for example, store the CSS selection string that is used to make the element visible. When a new element is clicked and you have to hide the visible element, you can use the same CSS selection string in $(...).slideUp to hide the element. Remember to declare the variable outside the functions so that it will keep its value between successive calls to the function.

If you want a perfect accordion list, you have to think about what happens when the user clicks the name of the course whose description is currently visible. If you've gotten this far, here is what will probably happen: the description will slide up, then immediately slide down again. What should happen is that it should slide up and stay there. That is, clicking the currently visible course should just hide the course information. It's not too hard to make the list work that way...