The first way is using effect callbacks, which are very similar to the Ajax callbacks you learned about in Chapter 4.. The second is using effect queues, which can force effects to wait
Trang 1The element we told to fade out is still visible—it’s only just begun animating That’s
because the interpreter doesn’t wait around; it schedules the frames of the animation,
and then moves on to the next line
script.aculo.us gives us two ways around this The first way is using effect callbacks,
which are very similar to the Ajax callbacks you learned about in Chapter 4 The second is
using effect queues, which can force effects to wait their turn behind other effects.
Callbacks
Both core effects and combination effects treat certain parameters passed into the
optionsargument as callbacks:
• beforeSetupand afterSetupare called before and after the “setup” stage of an
effect—in which the effect makes any necessary modifications to the element
itself
• beforeStartand afterStartare called before and after the first frame of an effect,
respectively
• beforeUpdateand afterUpdateare called before and after each frame of an effect.
• beforeFinishand afterFinishare called before and after the “finish” stage of an
effect, in which the effect reverts any changes it made in the “setup” stage
To ensure that our alertdialog appears after the effect is complete, we can use the
afterFinishcallback:
Effect.Fade('box', {
afterFinish: function(effect) { debugger; }
});
Figure 10-19 shows that the element is completely invisible before the debugger
starts
Trang 2Figure 10-19.By moving the debugger statement to a callback, we ensure that it runs after the effect has finished.
Taken together, these callbacks provide hooks into every single frame of an effect They’re the ultimate override Most of the time, afterFinishwill be all you need, but it’s nice to know the rest are there
Each callback takes the effect instance itself as the first argument Since the
Effect.Baseclass (from which all effects derive) defines some instance properties, you’re able to use these same properties in your callbacks:
Effect.Fade('box', {
afterFinish: function(effect) {
effect.element.remove();
}
});
You may also use several other properties: optionsto access the effect’s options, startOnand finishOnto access the integer timestamps that mark the effect’s duration, and currentFrameto access the number of the last frame rendered
Trang 3Effect queues address a subset of the collision problem described earlier Let’s wire up a
button to our box—one that will fade the box out when clicked:
<input type="button" name="go" value="Go" id="effect_button" />
<div id="box">
Lorem ipsum
</div>
<script type="text/javascript" charset="utf-8">
$('effect_button').observe('click', function() {
new Effect.Highlight('box');
});
</script>
Clicking the button pulses the box, just like we’d expect But clicking twice rapidly
fires two highlight effects—they get tangled almost immediately, leaving the element
per-manently yellow (see Figure 10-20)
Figure 10-20.Triggering Effect.Highlight twice in rapid succession leaves the element with a
permanent yellow background color.
Trang 4To be fair, it’s only doing what you tell it to do By default, an effect starts animating
as soon as it’s called But the savvy developer can manage the state of an effect—telling it when to wait and when not to run at all
When we click the button twice, we want the first effect to start right away, but the
second effect to start once the first is done The queueoption lets us do so:
new Effect.Highlight('box', { queue: ‘end’ });
The endvalue merely says to place the effect at the end of whatever effect is already running on the page, rather than the default parallel A third option, front, halts
what-ever effect is already playing in order to give the new effect priority
Now we can click the button as much as we want—if we click it ten times in the span
of a second, we’ll see ten highlight effects fire patiently, one after the other
Putting It All Together
We’re going to go through one last example in this chapter It pulls in some of the work we did in previous chapters and adds an effects-inspired garnish
In Chapters 4 and 5, we wrote the code to simulate a data provider—one that sup-plies ever-changing statistics for our fictional fantasy football game We hooked it up to
an Ajax poller that checks for new scores every 30 seconds and fires a custom event whenever it gets a response We laid all this groundwork without a clear example of when we’d be able to use it next
Similarly, in Chapter 7 we wrote a utility class for adding up numbers The use case
we had in mind was a data table in which each row had a numeric value—and a “total” row in the table footer that would show the sum of the values from all rows
Effects give us the final piece We’re going to build a game summary page for our fan-tasy football league It will display both teams’ scores, side by side, with a breakdown by player of where the points are coming from
Writing the Markup
First, let’s get this all onto the page in the form of markup Then we can style it to aes-thetic perfection
Create a new file called game.htmlwith scripttags pointing to prototype.jsand scriptaculous.js:
Trang 5<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>Game Page</title>
<script src="js/prototype.js" type="text/javascript"></script>
<script src="js/scriptaculous.js" type="text/javascript"></script>
</head>
<body>
</body>
</html>
In the body of the page, we’ll place some divs to serve as rough guides for where our
elements will go:
<div id="wrapper">
<h1>Box Score</h1>
<div id="teams">
<div id="team_1_container">
<h2>The Fighting Federalists</h2>
<! TABLE GOES HERE >
</div> <! #team_1_container >
<div id="team_2_container">
<h2>Washington's Generals</h2>
<! TABLE GOES HERE >
</div> <! #team_2_container >
</div> <! #teams >
</div> <! #wrapper >
Trang 6Finally, let’s look at the data table itself It’s going to have four columns: position, name, score, and summary (a short text description of the player’s stats) All these pieces
of data exist in the JSON feed except for the player’s name; instead, we’ll be using our
shorthand to refer to players by position (e.g., QB,WR1,RB2)
So one table might look like this:
<table id="team_1">
<thead>
<tr>
<! We can't fit the word "position" in this column, so let's
abbreviate and put the full word inside a "title" attribute >
<th class="pos" title="Position">Pos.</th>
<th>Name</th>
<th>Stats</th>
<th class="score">Points</th>
</tr>
</thead>
<! In accordance with the HTML spec, the TFOOT occurs _before_ the TBODY
in the markup, even though it's placed _after_ the TBODY visually >
<tfoot>
<tr>
<td colspan="3" class="total">Total</td>
<! This table cell will display the total It has an ID so that we
can grab it easily >
<td id="team_1_total" class="score"></td>
</tr>
</tfoot>
<tbody>
<! Each table row has the position shorthand (RB1, WR2, etc.) as a
class name Table cells have class names as hooks for both scripting and styling >
<tr class="QB">
<td class="pos">QB</td>
<td>Alexander Hamilton</td>
<td class="summary"></td>
<td class="score">0</td>
</tr>