CHAPTER 10

image

Using jQuery Effects

For the most part, jQuery UI contains the user interface (UI) functionality associated with jQuery, but the core library includes some basic effects and animations, and these are the topic of this chapter. Although I describe them as basic, they can be used to achieve some pretty sophisticated effects. The main focus is on animating the visibility of elements, but you can use these features to animate a range of CSS properties in a number of ways. Table 10-1 provides the summary for this chapter.

Table 10-1. Chapter Summary

Problem Solution Listing
Show or hide elements Use the show or hide method 1
Toggle the visibility of elements Use the toggle method 2, 3
Animate the visibility of elements Provide a timespan argument to the show, hide, or toggle method 4
Call a function at the end of an animation Provide a callback argument to the show, hide, or toggle method 5–7
Animate visibility along the vertically Use the slideDown, slideUp, or slideToggle method 8
Animate visibility using opacity Use the fadeIn, fadeOut, fadeToggle, or fadeTo method 9–11
Create a custom effect Use the animate method 12–14
Inspect the queue of effects Use the queue method 15, 16
Stop and clear the effect queue Use the stop or finish methods 17, 18
Insert a delay into the effect queue Use the delay method 19
Insert custom functions into the queue Use the queue method with a function argument and ensure that the next function in the queue is executed 20, 21
Disable the animation of effects Set the $.fx.off property to true 22

JQUERY CHANGES SINCE THE LAST EDITION

jQuery 1.9/2.0 defines a new finish method that is used to complete the current effect and clear the event queue. See the section “Stopping Effects and Clearing the Queue” for details.

Using the Basic Effects

The most basic effects are simply to show or hide elements. Table 10-2 describes the methods that you can use for this.

Table 10-2. Basic Effects Methods

Method Description
hide() Hides all of the elements in a jQuery object
hide(time)hide(time, easing) Hides the elements in a jQuery object over the specified period of time with an optional easing style
hide(time, function)hide(time, easing, function) Hides the elements in a jQuery object over the specified period of time with an optional easing style and a function that is called when the effect is complete
show() Shows all of the elements in a jQuery object
show(time)show(time, easing) Shows the elements in a jQuery object over the specified period of time with an optional easing style
show(time, function)show(time, easing, function) Shows the elements in a jQuery object over the specified period of time with an optional easing style and a function that is called when the effect is complete
toggle() Toggles the visibility of the elements in a jQuery object
toggle(time)toggle(time, easing) Toggles the visibility of the elements in a jQuery object over the specified period of time with an optional easing style
toggle(time, function)toggle(time, easing, function) Toggles the visibility of the elements in a jQuery object over the specified period of time with an optional easing style and a function that is called when the effect is complete
toggle(boolean) Toggles the elements in a jQuery object in one direction

Listing 10-1 shows the simplest of these effects, which is to use the show and hide methods without any arguments.

Listing 10-1.  Using the Show and Hide Methods Without Arguments

<!DOCTYPE html>
<html>
<head>
    <title>Example</title>
    <script src="jquery-2.0.2.js" type="text/javascript"></script>
    <link rel="stylesheet" type="text/css" href="styles.css"/>
    <script type="text/javascript">
        $(document).ready(function() {
            $("<button>Hide</button><button>Show</button>").appendTo("#buttonDiv")
                .click(function(e) {
                   if ($(e.target).text() == "Hide") {
                        $("#row1 div.dcell").hide();
                   } else {
                        $("#row1 div.dcell").show();
                   }
                   e.preventDefault();
                });
        });
    </script>
</head>
<body>
    <h1>Jacqui's Flower Shop</h1>
    <form method="post">
        <div id="oblock">
            <div class="dtable">
                <div id="row1" class="drow">
                    <div class="dcell">
                        <img src="aster.png"/><label for="aster">Aster:</label>
                        <input name="aster" value="0" required />
                    </div>
                    <div class="dcell">
                        <img src="daffodil.png"/><label for="daffodil">Daffodil:</label>
                        <input name="daffodil" value="0" required />
                    </div>
                    <div class="dcell">
                        <img src="rose.png"/><label for="rose">Rose:</label>
                        <input name="rose" value="0" required />
                    </div>
                </div>
                <div id="row2"class="drow">
                    <div class="dcell">
                        <img src="peony.png"/><label for="peony">Peony:</label>
                        <input name="peony" value="0" required />
                    </div>
                    <div class="dcell">
                        <img src="primula.png"/><label for="primula">Primula:</label>
                        <input name="primula" value="0" required />
                    </div>
                    <div class="dcell">
                        <img src="snowdrop.png"/><label for="snowdrop">Snowdrop:</label>
                        <input name="snowdrop" value="0" required />
                    </div>
                </div>
            </div>
        </div>
        <div id="buttonDiv"><button type="submit">Place Order</button></div>
    </form>
</body>
</html>

I manipulate the DOM (Domain Object Model) to add two button elements and provide a function to be called when either of them is clicked. This function uses the text method to figure out which button has been used and calls either the hide or show method. In both cases, I call this method on a jQuery object with the selector #row1 div.dcell, meaning that those div elements in the dcell class that are descendants of the element with the id of row1 will be made invisible or visible. Figure 10-1 illustrates what happens when I click the Hide button.

9781430263883_Fig10-01.jpg

Figure 10-1. Hiding elements with the hide element

Clicking the Show button calls the show method, restoring the hidden elements, as Figure 10-2 shows.

9781430263883_Fig10-02.jpg

Figure 10-2. Showing elements with the show method

It is hard to show transitions using figures, but there are a few points to note. The first is that the transition is immediate: there is no delay or effect, and the elements just appear and disappear. Second, calling hide on elements that are already hidden has no effect; nor does calling show on elements that are visible. Finally, when you hide or show an element, you also show or hide all of its descendants.

image Tip  You can select elements using the :visible and :hidden selectors. See Chapter 5 for details of the jQuery extension CSS selectors.

Toggling Elements

You can flip elements from being visible or hidden and back using the toggle method. Listing 10-2 gives an example.

Listing 10-2.  Using the toggle Method to Switch Element Visibility

...
<script type="text/javascript">
    $(document).ready(function() {
        $("<button>Toggle</button>").appendTo("#buttonDiv")
            .click(function(e) {
                $("div.dcell:first-child").toggle();
                e.preventDefault();
            });
    });
</script>
...

In Listing 10-2 I add a single button to the document, and when it is clicked, I use the toggle element to change the visibility of the div.dcell elements that are the first children of their parents. You can see the effect in Figure 10-3.

9781430263883_Fig10-03.jpg

Figure 10-3. Toggling the visibility of elements

image Tip  Notice that the structure of the document collapses around the hidden elements. If you want to hide the elements and leave space on the screen, then you can set the CSS visibility property to hidden.

Toggling in One Direction

You can pass a boolean argument to the toggle method to restrict the way that visibility is toggled. If you pass true as the argument, then only hidden elements will be shown (visible elements will not be hidden). If you pass false as the argument, then you get the opposite effect. Visible elements will be hidden, but hidden elements will not be made visible. Listing 10-3 shows the use of this flavor of the toggle method. The script in Listing 10-3 creates the effect shown in Figure 10-3.

Listing 10-3.  Using the toggle Method in One Direction

...
<script type="text/javascript">
    $(document).ready(function() {
        $("<button>Toggle</button>").appendTo("#buttonDiv")
            .click(function(e) {
                $("div.dcell:first-child").toggle(false);
                e.preventDefault();
            });
    });
</script>
...

Animating the Visibility of Elements

You can animate the process of showing and hiding elements by passing a time span to the show, hide, or toggle methods. The process of showing and hiding elements is then performed gradually, over the specified period. Table 10-3 shows the different time span arguments you can use.

Table 10-3. Time Span Arguments

Method Description
<number> Specifies  duration in milliseconds
slow A shorthand equivalent to 600 milliseconds
fast A shorthand equivalent to 200 milliseconds

Listing 10-4 shows how you can animate showing and hiding elements.

Listing 10-4.  Animating the Visibility of Elements

...
<script type="text/javascript">
    $(document).ready(function() {
          
        $("<button>Toggle</button>").appendTo("#buttonDiv")
            .click(function(e) {
                $("img").toggle("fast", "linear");
                e.preventDefault();
            });
              
    });
</script>
...

In Listing 10-4, I have used the fast value to specify that toggling the visibility of the img elements in the document should be done over a period of 600 milliseconds.

image Tip  When specifying a duration in milliseconds, be sure that the value is not quoted. That is, use $("img").toggle(500) and not $("img").toggle("500"). If you do use quotes, then the value will be ignored.

I have also provided an additional argument, which specifies the style of the animation, known as the easing style or easing function. Two easing styles are available, swing and linear. When animating with the swing style, the animation starts slowly, speeds up, and then slows down again as the animation reaches conclusion. The linear style maintains a constant pace throughout the animation. If you omit the argument, swing is used. You can see the effect of the animation as it hides elements in Figure 10-4. It is difficult to show animation in this way, but you’ll get a sense of what happens.

9781430263883_Fig10-04.jpg

Figure 10-4. Animating hiding elements

As the figure shows, the animation effect reduces the size of the image in both dimensions and decreases the opacity. At the end of the animation, the img elements are invisible. Figure 10-5 shows what happens if you click the Toggle button again to make the img elements visible.

9781430263883_Fig10-05.jpg

Figure 10-5. Animating showing elements

Using Effect Callbacks

You can supply a function as an argument to the show, hide, and toggle methods, and the function will be called when these methods finish performing their effect. This can be useful for updating other elements to reflect the change in status, as shown in Listing 10-5.

Listing 10-5.  Using an Event Callback

...
<script type="text/javascript">
    $(document).ready(function() {
  
        var hiddenRow = "#row2";
        var visibleRow = "#row1";
          
        $(hiddenRow).hide();
          
        function switchRowVariables() {
            var temp = hiddenRow;
            hiddenRow = visibleRow;
            visibleRow = temp;
        }
  
        function hideVisibleElement() {
            $(visibleRow).hide("fast", showHiddenElement);
        }
              
        function showHiddenElement() {
            $(hiddenRow).show("fast", switchRowVariables);
        }
          
        $("<button>Switch</button>").insertAfter("#buttonDiv button")
            .click(function(e) {
                hideVisibleElement();
                e.preventDefault();
            });
    });
</script>
...

image Tip  If you want to perform multiple sequential effects on a single element, then you can use regular jQuery method chaining. See the section “Creating and Managing the Effect Queue” for details.

  

To make Listing 10-5 clearer, I have broken down the effect activities into separate functions. To get set up, I hide one of the div elements that acts as a row in my CSS table layout and define two variables that I use to track which row is visible and which row is not. I add a button element to the document, and when this is clicked I call the hideVisibleElement function, which uses the hide method to animate hiding the visible row.

...
$(visibleRow).hide("fast",showHiddenElement);
...

I specify the name of the function I want performed when the effect has completed, in this case showHiddenElement.

image Tip  The callback function is not passed any arguments, but the this variable is set to the DOM element being animated. If multiple elements are being animated, then the callback function will be called once for each of them.

This function uses the show method to animate revealing the element, as follows:

...
$(hiddenRow).show("fast",switchRowVariables);
...

Once again, I specify a function to perform at the end of the effect. In this case, it is the switchRowVariables function, which shuffles the variables that track visibility so that you perform the effects on the right elements the next time the button is clicked. The result is that when the button is clicked, the current row is replaced by the hidden row, with a quick animation to make the transition less jarring to the user. Figure 10-6 shows the effect (although, once again, the true effect becomes apparent only when you load the example in the browser).

9781430263883_Fig10-06.jpg

Figure 10-6. Using callback functions to chain effects

You wouldn’t usually need to break out the individual functions as I did, so Listing 10-6 shows the same example rewritten using a terser set of inline functions.

Listing 10-6.  Using Inline Callback Functions

...
<script type="text/javascript">
    $(document).ready(function() {
          
        var hiddenRow = "#row2";
        var visibleRow = "#row1";
          
        $(hiddenRow).hide();
          
        $("<button>Switch</button>").insertAfter("#buttonDiv button")
            .click(function(e) {
                $(visibleRow).hide("fast", function() {
                    $(hiddenRow).show("fast", function() {
                        var temp = hiddenRow;
                        hiddenRow = visibleRow;
                        visibleRow = temp;
                    });
                });
                  
                e.preventDefault();
            });
    });
</script>
...

Creating Looping Effects

You can use the callback functions to produce effects that are performed in a loop. Listing 10-7 demonstrates.

Listing 10-7.  Using Callback Functions to Create a Looping Effect

...
<script type="text/javascript">
    $(document).ready(function() {
          
        $("<button>Toggle</button>").insertAfter("#buttonDiv button")
            .click(function(e) {
                performEffect();
                e.preventDefault();
            });
          
        function performEffect() {
            $("h1").toggle("slow", performEffect)
        }
    });
</script>
...

In this example, clicking the button leads to the performEffect function being executed. This function uses the toggle method to change the visibility of the h1 element in the document and passes itself as the callback argument. The result is that the h1 element loops between being visible and hidden, as shown in Figure 10-7.

9781430263883_Fig10-07.jpg

Figure 10-7. Performing an effect in a loop

image Tip  Some caution is required when using the current function as the callback function. Eventually you will exhaust the JavaScript call stack, and your script will stop working. The easiest way to solve this problem is with the setTimeout function, which will schedule a callback to your target function without nesting the function calls, like this: $("h1").toggle("slow", setTimeout( performEffect, 1)). It is actually pretty hard to exhaust the call stack, and it usually means leaving a page with animations running for a long time, but it is worth bearing in mind—however, it is something to be careful of when using the finish method, which I describe in the section “Stopping Effects and Clearing the Queue.”

USING EFFECTS RESPONSIBLY

To my mind, loops like this should be used sparingly and only when they serve a purpose (and by that, I mean a purpose for the user and not to show off your excellent jQuery effect skills). In general, the impact of any kind of effect should be carefully considered. It may look great during development, but injudicious use of effects destroys the user’s enjoyment of your web application, especially if it is an application he or she uses every day.

As a simple example, I am a keen runner (keen but not any good). I used to have a runner’s wristwatch that collects data about my heart rate, speed, distance, calories burned, and 100 other data points. At the end of a run, I would upload the data to the manufacturer’s web site for storage and analysis.

Here’s where the pain started. Every time I clicked a button on the page, the content I wanted would be revealed through a long effect. I know the browser has received the data I want because I can see it being gradually revealed, but it is a couple of seconds before I can read it. A couple of seconds may not sound like a lot, but it is, especially when I want to look at between five and ten different data items at any time.

I am sure that the designer of the application thought the effects were nice and that they enhanced the experience. But they don’t. They sucked so much that it made using the application a teeth-grinding experience—so much so that since the last edition of this book, I purchased a competitor product.

This web application for the (now discarded) watch had some useful data analysis tools, but I got to the point where it annoyed me so much that I was willing to pay several hundred dollars to get a replacement. I could be a champion marathon runner by now if not for those effects (and maybe the beer and pizza I find myself consuming with shocking frequency).

If you think I am exaggerating (about the effects . . . you can take my word about the pizza), then take one of the listings in this chapter and set the time span to two seconds. Then get a feel for how long that is when you are waiting for the effect to complete.

My advice is that all effects should be used sparingly. I tend to use them only when making a change to the DOM that would be jarring otherwise (elements suddenly disappearing from the page). When I do use them, I keep the time spans short, typically 200 milliseconds. I never employ endless loops. That’s just a recipe for giving the user a headache. I urge you to take the time to think about how your users engage with your application or site and remove anything that doesn’t make the tasks at hand easier to perform. Glossy sites are good, but glossy sites that are usable are great.

Using the Slide Effects

jQuery has a set of effects that slide elements on and off the screen. Table 10-4 describes the methods for this.

Table 10-4. Slide Effects Methods

Method Description
slideDown()slideDown((time, function)slideDown(time, easing, function) Show elements by sliding them down
slideUp()slideUp(time, function)slideUp(time, easing, function) Hide elements by sliding them up
slideToggle()slideToggle(time, function)slideToggle(time, easing, function) Toggle the visibility of elements by sliding them up and down

These methods animate an element in the vertical axis. The arguments to these methods are as for the basic effect.  You can elect to provide a time span, an easing style, and a callback function. Listing 10-8 shows the slide effects in use.

Listing 10-8.  Using the Slide Effects

...
<script type="text/javascript">
    $(document).ready(function() {
        $("<button>Toggle</button>").insertAfter("#buttonDiv button")
            .click(function(e) {
                $("h1").slideToggle("fast");
                e.preventDefault();
            });
    });
</script>
...

In this script, I use the slideToggle method to toggle the visibility of the h1 element. You can see the effect in Figure 10-8.

9781430263883_Fig10-08.jpg

Figure 10-8. Using the slide effect to reveal an element

The figure shows the h1 element being made visible. The elements are clipped, rather than scaled, because jQuery creates the effect by manipulating the height of the element. You can see what I mean by this in Figure 10-9.

9781430263883_Fig10-09.jpg

Figure 10-9. jQuery creating an effect by manipulating the height of the elements

This figure shows a close-up of the h1 element as it is made visible. You can see that the size of the text doesn’t change, only the amount of it that is shown. However, this isn’t true for images, because the browser scales them automatically. If you look really closely, you can see that the entire background image is always shown, but it is scaled down to fit the height.

Using the Fade Effects

The fade effect methods show and hide elements by reducing their opacity (or, if you prefer, increasing their transparency). Table 10-5 describes the fade effect methods.

Table 10-5. Fade Effects Methods

Method Description
fadeOut()fadeOut(timespan)fadeOut(timespan, function)fadeOut(timespan, easing, function) Hide elements by decreasing opacity
fadeIn()fadeIn(timespan)fadeIn(timespan, function)fadeIn(timespan, easing, function) Show elements by increasing opacity
fadeTo(timespan, opacity)fadeTo(timespan, opacity, easing, function) Change the opacity to the specified level
fadeToggle()fadeToggle(timespan)fadeToggle(timespan, function)fadeToggle(timespan, easing, function) Toggle the visibility of elements using opacity

The fadeOut, fadeIn, and fadeToggle methods are consistent with the other effect methods.  You can provide a time span, an easing style, and a callback function, just as in the previous listings. Listing 10-9 demonstrates how to use fading.

Listing 10-9.  Showing and Hiding Elements by Fading

...
<script type="text/javascript">
    $(document).ready(function() {
        $("<button>Toggle</button>").insertAfter("#buttonDiv button")
            .click(function(e) {
                $("img").fadeToggle();
                e.preventDefault();
            });
    });
</script>
...

I have applied the fadeToggle method to the img elements in the document, in part to demonstrate one of the limitations of this effect. Figure 10-10 shows what happens when you hide the elements.

9781430263883_Fig10-10.jpg

Figure 10-10. Using the fade effect

The fade effect operates only on opacity, unlike the other effects that change the size of the selected elements as well. This means you get a nice smooth fade effect until the elements are completely transparent, at which point jQuery hides them and the page snaps into a new layout. This last stage can be somewhat jarring if not used carefully.

Fading to a Specific Opacity

You can use the fadeTo method to fade elements to a particular opacity. The range of opacity values is a number within the range of 0 (completely transparent) to 1 (completely opaque). The visibility of the elements isn’t changed, so you avoid the snap of the page layout I mentioned. Listing 10-10 shows the use of the fadeTo method.

Listing 10-10.  Fading to a Specific Opacity

...
<script type="text/javascript">
    $(document).ready(function() {
        $("<button>Fade</button>").insertAfter("#buttonDiv button")
            .click(function(e) {
                $("img").fadeTo("fast", 0);
                e.preventDefault();
            });
    });
</script>
...

In this example, I have specified that the img elements should be faded until they are completely transparent. This has the same effect as the fadeOut method but doesn’t hide the elements at the end of the transition. Figure 10-11 shows the effect.

9781430263883_Fig10-11.jpg

Figure 10-11. Fading out an element with the fadeTo method

You don’t have to fade elements to the extremes of the opacity range.  You can specify intermediate values as well, as Listing 10-11 demonstrates.

Listing 10-11.  Fading to a Specific Opacity

...
<script type="text/javascript">
    $(document).ready(function() {
        $("<button>Fade</button>").insertAfter("#buttonDiv button")
            .click(function(e) {
                $("img").fadeTo("fast", 0.4);
                e.preventDefault();
            });
    });
</script>
...

You can see the effect in Figure 10-12.

9781430263883_Fig10-12.jpg

Figure 10-12. Fading to a specific opacity

Creating Custom Effects

jQuery doesn’t limit you to the basic slide and fade effects.  You can create your own as well. Table 10-6 shows the methods that you use for this process.

Table 10-6. Custom Effects Methods

Method Description
animate(properties)animate(properties, time)animate(properties, time, function)animate(properties, time, easing, function) Animates one or more CSS properties, with an optional time span, easing style, and callback function
animate(properties, options) Animates one or more CSS properties, specifying the options as a map

jQuery can animate any property that accepts a simple numeric value (e.g., the height property).

image Note  Being able to animate numeric CSS properties means you can’t animate colors. There are a few ways to address this. The first (and to my mind best) solution is to use jQuery UI, which I describe in Part IV of this book. If you don’t want to use jQuery UI, then you might like to consider using the native browser support for CSS animations. The performance of these is pretty good, but the support is patchy currently and nonexistent in older browser versions. For details of CSS animation, see my book The Definitive Guide to HTML5, which is also published by Apress. The approach that I like least is using a jQuery plug-in. Animating colors is difficult to get right, and I have yet to find a plug-in that I am entirely satisfied with, but the most reliable I have found is available from https://github.com/jquery/jquery-color.

You supply a set of properties that you want to animate as a map object, and, if you want, you can do the same for the options you want to set. Listing 10-12 shows a custom animation.

Listing 10-12.  Using a Custom Animation

...
<script type="text/javascript">
    $(document).ready(function() {
                  
        $("form").css({"position": "fixed", "top": "70px", "z-index": "2"});
        $("h1").css({"position": "fixed", "z-index": "1", "min-width": "0"});
          
        $("<button>Animate</button>").insertAfter("#buttonDiv button")
            .click(function(e) {
                  
                $("h1").animate({
                    height: $("h1").height() + $("form").height() + 10,
                    width: ($("form").width())
                });
                  
                e.preventDefault();
            });
    });
</script>
...

In Listing 10-12, I want to animate dimensions of the h1 element so that its background image extends behind the form element. Before I can do this, I need to make some changes to the CSS (Cascading Style Sheets) for the affected elements. I could do this using the style sheet I defined in Chapter 3, but since this is a book on jQuery, I have chosen to use JavaScript. To make the animation easier to manage, I have positioned both the form and h1 elements using the fixed mode and have used the z-index property to ensure that the h1 element is displayed below the form.

I have added a button to the document that calls the animate method when clicked. I have chosen to animate the height and width properties, using information obtained via other jQuery methods. You can see the effect of the animation in Figure 10-13.

9781430263883_Fig10-13.jpg

Figure 10-13. Performing a custom animation

I have shown only the start and end states in the figure, but jQuery provides a smooth transition just like with the other effects, and you can exert control over the transition by specifying a time span and an easing style.

Using Absolute Target Property Values

Notice that you specify only the final values for the animation. The start point for jQuery custom animations is the current values of the properties being animated. I used values I derived from other jQuery methods, but you have some other choices. First, and most obvious, is that  you can use absolute values, as shown in Listing 10-13.

Listing 10-13.  Performing a Custom Animation Using Absolute Values

...
<script type="text/javascript">
    $(document).ready(function() {
                  
        $("form").css({"position": "fixed", "top": "70px", "z-index": "2"});
        $("h1").css({"position": "fixed", "z-index": "1", "min-width": "0"});
          
        $("<button>Animate</button>").insertAfter("#buttonDiv button")
            .click(function(e) {
                $("h1").animate({
                    left: 50,
                    height: $("h1").height() + $("form").height() + 10,
                    width: ($("form").width())
                });
                               
                e.preventDefault();
            });
    });
</script>
...

In Listing 10-13, I have added the left property to the animation, specifying an absolute value of 50 (which will be taken as 50 pixels). This shifts the h1 element to the right. Figure 10-14 shows the outcome of the animation.

9781430263883_Fig10-14.jpg

Figure 10-14. Creating a custom animation with a fixed final property value

Using Relative Target Property Values

You can also specify your animation targets using relative values. You specify an increase by prefixing a value with += and a decrease with -=. Listing 10-14 shows the use of relative values.

Listing 10-14.  Using Relative Values in a Custom Animation Effect

...
<script type="text/javascript">
    $(document).ready(function() {
                  
        $("form").css({"position": "fixed", "top": "70px", "z-index": "2"});
        $("h1").css({"position": "fixed", "z-index": "1", "min-width": "0"});
          
        $("<button>Animate</button>").insertAfter("#buttonDiv button")
            .click(function(e) {
                $("h1").animate({
                    height: "+=100",
                    width: "-=700"
                });
                                  
                e.preventDefault();
            });
    });
</script>
...

Creating and Managing the Effect Queue

When you use effects, jQuery creates a queue of the animations that it has to perform and works its way through them. There are a set of methods you can use to get information about the queue or take control of it, as described in Table 10-7.

Table 10-7. Effects Queue Methods

Method Description
queue() Returns the queue of effects to be performed on the elements in the jQuery object
queue(function) Adds a function to the end of the queue
dequeue() Removes and executes the first item in the queue for the elements in the jQuery object
stop()stop(clear)stop(clear, jumpToEnd) Stops the current animation
finish() Stops the current animation and clears any queued animations
delay(time) Inserts a delay between effects in the queue

You create a queue of effects by chaining together calls to effect-related methods, as shown in Listing 10-15.

Listing 10-15.  Creating an Effect Queue

...
<script type="text/javascript">
    $(document).ready(function() {
          
        $("form").css({"position": "fixed", "top": "70px", "z-index": "2"});
        $("h1").css({"position": "fixed", "z-index": "1", "min-width": "0"});
          
        var timespan = "slow";
  
        cycleEffects();
          
        function cycleEffects() {
                $("h1")
                .animate({left: "+=100"}, timespan)
                .animate({left: "-=100"}, timespan)
                .animate({height: 223,width: 700}, timespan)
                .animate({height: 30,width: 500}, timespan)
                .slideUp(timespan)
                .slideDown(timespan, cycleEffects);
        }
    });
</script>
...

This script in Listing 10-15 uses regular jQuery method chaining to string together a series of effects on the h1 element. The last effect uses the cycleEffects function as the callback, which starts the process over again. This is a pretty annoying sequence. It is hypnotic for a moment and then a little irritating, and then it tips right over into the kind of effect that brings on a headache. But it does create a queue of effects, which is what I need to demonstrate the queue features.

image Note  I could have used the callback functions to achieve the same effect, but that doesn’t create the effect queue, because the function that starts the next animation isn’t executed until the previous animation has completed. When you use regular method chaining, as in this example, all of the methods are evaluated, and the animation effects are placed in the queue. The limitation of using method chaining is that you are limited to working with the current selection. When using callbacks, you can string together sequences that involve entirely unrelated elements.

Displaying the Items in the Effect Queue

You can use the queue method to inspect the contents of the effects queue. This is not entirely helpful because the queue contains one of two types of data object. If an effect is being executed, then the corresponding item in the queue is the string value inprogress. If the effect is not being executed, the item in the queue is the function that will be invoked (although the function doesn’t reveal which animation function will be performed). That said, inspecting the content is a good place to start with the queue, and Listing 10-16 shows how it can be done.

Listing 10-16.  Inspecting the Contents of the Effect Queue

...
<script type="text/javascript">
    $(document).ready(function () {
  
        $("h1").css({ "position": "fixed", "z-index": "1", "min-width": "0" });
        $("form").remove();
        $("<table border=1></table>")
            .appendTo("body").css({
                position: "fixed", "z-index": "2",
                "border-collapse": "collapse", top: 100
            });
  
        var timespan = "slow";
  
        cycleEffects();
        printQueue();
  
        function cycleEffects() {
            $("h1")
            .animate({ left: "+=100" }, timespan)
            .animate({ left: "-=100" }, timespan)
            .animate({ height: 223, width: 700 }, timespan)
            .animate({ height: 30, width: 500 }, timespan)
            .slideUp(timespan)
            .slideDown(timespan, cycleEffects);
        }
  
        function printQueue() {
            var q = $("h1").queue();
            var qtable = $("table");
            qtable.html("<tr><th>Queue Length:</th><td>" + q.length + "</td></tr>");
            for (var i = 0; i < q.length; i++) {
                var baseString = "<tr><th>" + i + ":</th><td>";
                if (q[i] == "inprogress") {
                    $("table").append(baseString + "In Progress</td></tr>");
                } else {
                    $("table").append(baseString + q[i] + "</td></tr>");
                }
            }
            setTimeout(printQueue, 500);
        }
    });
</script>
...

I don’t need the form element in this example, so I have removed it from the DOM and replaced it with a simple table that I’ll use to display the contents of the effect queue. I have added a repeating function called printQueue that calls the queue method and displays the number of items and a little detail about each of them in the table. As I say, the items in the queue are not especially useful in their own right, but they do give you an overall picture of what is going on. Figure 10-15 shows how jQuery progresses through the effects queue.

9781430263883_Fig10-15.jpg

Figure 10-15. Inspecting the contents of the queue

Listing 10-16 is hard to portray in static images. I recommend you load the document into a browser to see for yourself. When the cycleEffects function is first called, there are six items in the effects queue, the first of which is shown as being in progress. The others are instances of an anonymous function that manages an animation. After each effect is completed, jQuery removes the item from the queue. At the end of the last effect, the cycleEffects function is called again, which puts six items in the queue again.

Stopping Effects and Clearing the Queue

You can use the stop and finish methods to interrupt the effect that jQuery is currently performing. For the stop method, you can provide two optional arguments to this method, both of which are boolean values. If you pass true as the first argument, then all of the other effects are removed from the queue and will not be performed. If you pass true as the second argument, then the CSS properties that are being animated by the current animation will be immediately set to their final values.

The default value for both arguments is false, which means that only the current effect is removed from the queue and that the properties that were being animated are left at the values they were set to at the moment the effect was interrupted. If you don’t clear the queue, jQuery will move on to the next effect and begin executing it as normal. Listing 10-17 provides an example of using the stop method.

Listing 10-17.  Using the stop Method

...
<script type="text/javascript">
    $(document).ready(function () {
  
        $("h1").css({ "position": "fixed", "z-index": "1", "min-width": "0" });
        $("form").remove();
  
        $("<table border=1></table>")
            .appendTo("body").css({
                position: "fixed", "z-index": "2",
                "border-collapse": "collapse", top: 100
            });
  
        $("<button>Stop</button><button>Start</button>")
          .appendTo($("<div/>").appendTo("body")
            .css({
                position: "fixed", "z-index": "2",
                "border-collapse": "collapse", top: 100, left: 200
            })).click(function (e) {
                $(this).text() == "Stop" ? $("h1").stop(true, true) : cycleEffects();
            });
  
        var timespan = "slow";
  
        cycleEffects();
        printQueue();
  
        function cycleEffects() {
            $("h1")
            .animate({ left: "+=100" }, timespan)
            .animate({ left: "-=100" }, timespan)
            .animate({ height: 223, width: 700 }, timespan)
            .animate({ height: 30, width: 500 }, timespan)
            .slideUp(timespan)
            .slideDown(timespan, cycleEffects);
        }
  
        function printQueue() {
            var q = $("h1").queue();
            var qtable = $("table");
            qtable.html("<tr><th>Queue Length:</th><td>" + q.length + "</td></tr>");
  
            for (var i = 0; i < q.length; i++) {
                var baseString = "<tr><th>" + i + ":</th><td>";
                if (q[i] == "inprogress") {
                    $("table").append(baseString + "In Progress</td></tr>");
                } else {
                    $("table").append(baseString + q[i] + "</td></tr>");
                }
            }
            setTimeout(printQueue, 500);
        }
    });
</script>
...

image Tip  When you call the stop method, any callback associated with the current effect will not be executed. When you use the stop method to clear the queue, no callback associated with any of the effects in the queue will be executed.

  

To demonstrate the stop method, I have added two buttons to the document. When the Stop button is clicked, I call the stop method, passing in two true arguments. This has the effect of clearing the rest of the effect queue and snapping the element to the target values for the property that was being animated. Since callback functions are not invoked when the stop method is used, the loop of cycleEffects method calls is broken, and animation is brought to a halt. When the Start button is clicked, the cycleEffects method is called and the animation resumes.

image Tip  Clicking the Start button when the animations are running doesn’t confuse jQuery. It just adds the effects used by the cycleEffects method to the effects queue. The use of callbacks means that the size of the queue will jump around a little, but in terms of the animations, everything continues as normal.

The finish method has a similar effect to calling stop(true, true), but differs in the way that it handles the CSS properties that are being animated. When calling stop(true, true), the CSS properties that are being animated by the current effect jump to their final values, but when using the finish method, the CSS properties being animated by the current effect and all of the queued effects jump to their final values. You can see how I have applied the finish method in Listing 10-18.

Listing 10-18.  Using the finish method

...
<script type="text/javascript">
    $(document).ready(function () {
  
        $("h1").css({ "position": "fixed", "z-index": "1", "min-width": "0" });
        $("form").remove();
  
        $("<table border=1></table>")
            .appendTo("body").css({
                position: "fixed", "z-index": "2",
                "border-collapse": "collapse", top: 100
            });
  
        var finishAnimations = false;
  
        $("<button>Stop</button><button>Start</button>")
          .appendTo($("<div/>").appendTo("body")
            .css({
                position: "fixed", "z-index": "2",
                "border-collapse": "collapse", top: 100, left: 200
            })).click(function (e) {
                if ($(this).text() == "Stop") {
                    finishAnimations = true;
                    $("h1").finish();
                } else {
                    finishAnimations = false;
                    cycleEffects();
                }
            });
  
        var timespan = "slow";
  
        cycleEffects();
        printQueue();
  
        function cycleEffects() {
            $("h1")
            .animate({ left: "+=100" }, timespan)
            .animate({ left: "-=100" }, timespan)
            .animate({ height: 223, width: 700 }, timespan)
            .animate({ height: 30, width: 500 }, timespan)
            .slideUp(timespan)
            .slideDown(timespan, function () {
                if (!finishAnimations) {
                    cycleEffects();
                }
            });
        }
  
        function printQueue() {
            var q = $("h1").queue();
            var qtable = $("table");
            qtable.html("<tr><th>Queue Length:</th><td>" + q.length + "</td></tr>");
  
            for (var i = 0; i < q.length; i++) {
                var baseString = "<tr><th>" + i + ":</th><td>";
                if (q[i] == "inprogress") {
                    $("table").append(baseString + "In Progress</td></tr>");
                } else {
                    $("table").append(baseString + q[i] + "</td></tr>");
                }
            }
            setTimeout(printQueue, 500);
        }
    });
</script>
...

When using the finish method, care must be taken with effect loops, such as the one in Listing 10-18. In order to determine the final CSS values for all of the animated properties, the finish method needs to execute all of the effects—albeit without any time delays—and that means that any callback functions are executed as well.

In Listing 10-18, the last effect defined in the cycleEffects function sets up the next effect loop, as follows:

...
.slideDown(timespan, cycleEffects);
...

The finish method doesn’t prevent new effects being added to the queue and it doesn’t keep track of the state of the queue when it is called. This means that the finish method will call the cycleEffects method, which adds effects to the queue, which the finish method then executes, triggering the callback, which adds effects to the queue . . . and so on. The overall effect is to immediately exhaust the JavaScript call stack.

To avoid this, I have added a variable called finishAnimations, which I set in response to the button elements being clicked and I check before adding the next set of effects to the queue, as follows:

...
.slideDown(timespan, function () {
    if (!finishAnimations) {
        cycleEffects();
    }
});
...

Inserting a Delay into the Queue

You can use the delay method to introduce a pause between two effects in the queue. The argument to this method is the number of milliseconds that the delay should last for. Listing 10-19 shows the use of this method to introduce one-second delays into the effect queue.

Listing 10-19.  Using the delay Method

...
function cycleEffects() {
    $("h1")
    .animate({ left: "+=100" }, timespan)
    .animate({ left: "-=100" }, timespan)
    .delay(1000)
    .animate({ height: 223, width: 700 }, timespan)
    .animate({ height: 30, width: 500 }, timespan)
    .delay(1000)
    .slideUp(timespan)
    .slideDown(timespan, function () {
        if (!finishAnimations) {
            cycleEffects();
        }
    });
}
...

Inserting Functions into the Queue

You can add your own functions into the queue using the queue method, and they will be executed just as the standard effect methods are. You can use this feature to start other animations, gracefully exit a chain of animations based on an external variable, or, well, do anything that you need. Listing 10-20 contains an example.

Listing 10-20.  Inserting a Custom Function into the Queue

...
function cycleEffects() {
    $("h1")
    .animate({ left: "+=100" }, timespan)
    .animate({ left: "-=100" }, timespan)
    .queue(function () {
        $("body").fadeTo(timespan, 0).fadeTo(timespan, 1);
        $(this).dequeue();
    })
    .delay(1000)
    .animate({ height: 223, width: 700 }, timespan)
    .animate({ height: 30, width: 500 }, timespan)
    .delay(1000)
    .slideUp(timespan)
    .slideDown(timespan, function () {
        if (!finishAnimations) {
            cycleEffects();
        }
    });
}
...

The this variable is set to the jQuery object that the method was called on. This is useful because you must make sure to call the dequeue method at some point in your function in order to move the queue onto the next effect or function. In this example, I used the queue method to add a function that fades the body element to fully transparent and back.

image Tip  The effects I added in the custom function are added to the effect queues for the body element. Each element has its own queue, and you can manage them independently of one another. If you want to animate multiple properties on the same elements whose queue you are operating on, then you just use the animate method. Otherwise, your effects will just be added to the queue in sequence.

Alternatively, you can accept a single argument to the function, which is the next function in the queue. In this situation, you must invoke the function to move the queue to the next effect, as shown in Listing 10-21.

Listing 10-21.  Using the Argument Passed to the Custom Function

...
function cycleEffects() {
    $("h1")
    .animate({ left: "+=100" }, timespan)
    .animate({ left: "-=100" }, timespan)
    .queue(function (nextFunction) {
        $("body").fadeTo(timespan, 0).fadeTo(timespan, 1);
        nextFunction();
    })
    .delay(1000)
    .animate({ height: 223, width: 700 }, timespan)
    .animate({ height: 30, width: 500 }, timespan)
    .delay(1000)
    .slideUp(timespan)
    .slideDown(timespan, function () {
        if (!finishAnimations) {
            cycleEffects();
        }
    });
}
...

image Caution  The effect sequence will stall if you don’t invoke the next function or call the dequeue method.

Enabling and Disabling Effect Animations

You can disable the animation of effects by setting the value of the $.fx.off property to true, as shown in Listing 10-22.

Listing 10-22.  Disabling Animations

...
<script type="text/javascript">
    $(document).ready(function () {
  
        $.fx.off = true;
  
        $("h1").css({ "position": "fixed", "z-index": "1", "min-width": "0" });
        $("form").remove();
  
        // ... statements omitted for brevity...
  
    });
</script>
...

When animations are disabled, calls to effect methods cause the elements to snap to their target property values immediately. Time spans are ignored, and there are no intermediate animations. Looping sets of effects will quickly hit the call stack limit when animations are disabled. To avoid this, use the setTimeout method, as described earlier in this chapter.

Summary

In this chapter, I showed you how to use the jQuery effect features. The built-in effect methods are mostly for making elements visible and invisible in different ways, but you can go beyond this and animate any numeric CSS property. You can also dig into the effect queue and take more control over the sequence of effects that are applied to elements. In Chapter 11, I refactor the example document to show you how some of the basic jQuery features covered in this and earlier chapters can be combined.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.41.148