The 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 options argument as callbacks: • beforeSetup and afterSetup are called before and after the “setup” stage of an effect—in which the effect makes any necessary modifications to the element itself. • beforeStart and afterStart are called before and after the first frame of an effect, respectively. • beforeUpdate and afterUpdate are called before and after each frame of an effect. • beforeFinish and afterFinish are 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 alert dialog appears after the effect is complete, we can use the afterFinish callback: Effect.Fade('box', { afterFinish: function(effect) { debugger; } }); Figure 10-19 shows that the element is completely invisible before the debugger starts. CHAPTER 10 ■ INTRODUCTION TO SCRIPT.ACULO.US EFFECTS 241 Figure 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, afterFinish will 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.Base class (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: options to access the effect’s options, startOn and finishOn to access the integer timestamps that mark the effect’s duration, and currentFrame to access the number of the last frame rendered. CHAPTER 10 ■ INTRODUCTION TO SCRIPT.ACULO.US EFFECTS242 Queues Effect 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. CHAPTER 10 ■ INTRODUCTION TO SCRIPT.ACULO.US EFFECTS 243 To 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 queue option lets us do so: new Effect.Highlight('box', { queue: ‘end’ }); The end value 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.html with script tags pointing to prototype.js and scriptaculous.js: CHAPTER 10 ■ INTRODUCTION TO SCRIPT.ACULO.US EFFECTS244 <!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 <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 > CHAPTER 10 ■ INTRODUCTION TO SCRIPT.ACULO.US EFFECTS 245 Finally, 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> CHAPTER 10 ■ INTRODUCTION TO SCRIPT.ACULO.US EFFECTS246 . 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. a new file called game.html with script tags pointing to prototype. js and scriptaculous.js: CHAPTER 10 ■ INTRODUCTION TO SCRIPT .ACULO. US EFFECTS244 <!DOCTYPE html PUBLIC "-//W3C//DTD. element itself. • beforeStart and afterStart are called before and after the first frame of an effect, respectively. • beforeUpdate and afterUpdate are called before and after each frame of an effect. • beforeFinish and