4

HTML & CSS:

<a href="#">link</a>
<img src="http://2.bp.blogspot.com/_OIHTbzY7a8I/TOaiTKLqszI/AAAAAAAAAHM/eb3iiOqxzKg/s640/Auto_Audi_Audi_concept_car_005130_.jpg" />
<div></div>

img { display: none; }
a { display: block; }

JS:

$("a").click(function(){
    $.ajax({
        url: "test.php",
        contentType: "html",
        beforeSend: function(){
            $("img").fadeIn(600, function(){
                $("div").append(" | beforeSend finished | ");
            });
        },
        error: function(){
            $("div").append(" | error | ");
        }
    });
    return false
});

The problem is, error function starts, before animation in beforeSend function finishes.

Here is working example http://jsfiddle.net/H4Jtk/2/

error should begin to work only when beforeSend is finished. How to do this?

I use beforeSend function to start animation of the blocks when ajax starts. I can't remove it.

Thanks.

James
  • 42,081
  • 53
  • 136
  • 161

3 Answers3

10

You can use a custom event to wait until the animation is complete, like this:

$("a").click(function(){
    var img = $("img"),
        div = $("div");
    $.ajax({
        url: "test.php",
        contentType: "html",
        beforeSend: function(){
            img.fadeIn(600, function(){
                div.append(" | beforeSend finished | ");
                div.trigger("animationComplete");
            });
        },
        error: function(){
            if(img.is(':animated')) {
                div.one("animationComplete", function() {
                    div.append(" | error | ");
                });
            } else {
                div.append(" | error | ");
            }
        }
    });
    return false
});

Notes:

  1. jQuery.one() attaches an event handler that fires once and is then removed.
  2. jQuery.is(':animated') is a custom selector provided by jQuery to determine if an element is animated using jQuery's built-in animation methods (eg fadeIn).

New jsFiddle: http://jsfiddle.net/pgjHx/


In the comments, Happy has asked how to handle multiple animations. One way would be to add a condition to the animationComplete event handler to see if animation is ongoing. For example:

$("a").click(function(){
    var img = $("img"),
        div = $("div");

    $.ajax({
        url: "test.php",
        contentType: "html",
        beforeSend: function(){
            img.fadeIn(600, function(){
                div.append(" | beforeSend finished | ");
                div.trigger("animationComplete");
            });

            // Add another animation just to demonstrate waiting for more than one animation
            img.animate({
                marginTop: '-=5',
                opacity: '-=.5'
            }, 700, function() {    
                div.trigger("animationComplete");
            });
        },
        error: function(){
            if(img.is(":animated")) {
                // Note I've switched to bind, because it shouldn't be unbound
                // until all animations complete
                div.bind("animationComplete", function() {
                    if(img.is(":animated")) {
                        return;
                    }
                    div.append(" | error | ");
                    div.unbind("animationComplete");
                });
            } else {
                div.append(" | error | ");
            }
        }
    });
    return false
});
eyelidlessness
  • 62,413
  • 11
  • 90
  • 94
  • I would like to use it without is(:animated) http://jsfiddle.net/H4Jtk/4/ works good – James Mar 06 '11 at 20:07
  • +1 for is(':animated'), could you tell where can i find more on this event 'animationComplete', just googled it but didnt find anything useful. – Praveen Prasad Mar 06 '11 at 20:07
  • 1
    @Happy, you could either queue the events (see queue options on http://api.jquery.com/animate/) and then trigger the 'animationComplete' event in the last animation, or you could maintain some kind of a counter of active animations (eg add `var animationCount = 0` to the top of the click event handler, then add `animationCount++` above each call to an animation, then add `animationCount--; if(animationCount > 0) return` to the beginning of the `animationComplete` handler). – eyelidlessness Mar 06 '11 at 20:14
  • @Praveen, I am just creating a custom event handler and a custom event name; there is no native `animationComplete` event in the DOM or in jQuery, I just chose that as a descriptive name. – eyelidlessness Mar 06 '11 at 20:14
  • @eyelidlessness u're a rockstar! – James Mar 06 '11 at 20:16
  • @Happy, I've reconsidered my comment discussing multiple animations and think I've gotten more complicated than necessary. I'll edit my answer to give a little more detail of a simpler solution. – eyelidlessness Mar 06 '11 at 20:17
  • @eye: oh got you, you are triggering that event when animation completes. I thought its an inbuilt jQuery event. – Praveen Prasad Mar 06 '11 at 20:22
  • people should give you more +++ – James Mar 06 '11 at 20:23
  • @Happy, please see my edit, below the horizontal rule, for handling multiple animations. Also note, in production you'd presumably want to use less broad selectors than `img` and `div`, I'm sure this is just for this isolated test case, yes? – eyelidlessness Mar 06 '11 at 20:23
  • @Happy, you can give me more + by "accepting" my answer by clicking the checkmark next to it. – eyelidlessness Mar 06 '11 at 20:24
1

you can do this

//run your animation on link click, and when animation gets completed, // then start your ajax request.

$("a").click(function () {
    $("img").fadeIn(600, function () {
        $("div").append(" | beforeSend finished | ");
         $.ajax({
                    url: "test.php",
                    contentType: "html",
                    error: function () {
                        $("div").append(" | error | ");
                    }
                });
                return false
            });
    });
});
Praveen Prasad
  • 31,561
  • 18
  • 73
  • 106
-1

The thing is that "beforeSend" is finished - it will exit long before that "fadeIn" finishes. I don't know what you're trying to accomplish so it's hard to offer a solution. You'd have to wait to start the ajax operation until after the animation sequence (the "fadeIn") completes if you wanted to guarantee that to happen first.

Pointy
  • 405,095
  • 59
  • 585
  • 614
  • I use beforeSend function to start animation of the blocks when ajax starts. When animation is finished, I show error message on data on success via backwards animation. Now its clearer? – James Mar 06 '11 at 20:02
  • Happy is trying to accomplish: 1. the XHR call firing immediately; 2. the `beforeSend` handler taking care of actions which take place at the beginning of the XHR request; and 3. the `error` event waiting for the `beforeSend` actions to complete before making DOM changes out of order. This can be accomplished with custom events without delaying the XHR request, per my answer. – eyelidlessness Mar 06 '11 at 20:04