April 29, 2012

How do I animate in jQuery without stacking callbacks?

Question by yuval

Let’s say I have three divs, and I’d like each to animate once the previous one is done. Currently, I write this:

$('div1').fadeOut('slow', function() {
    $('div2').fadeOut('slow', function() {
        $('div3').fadeOut('slow');
    });
});

Which is ugly, but manageable.

Now imagine I have 10 different animations that need to happen one after the other on different elements. Suddenly the code gets so clunky that it’s extremely hard to manage…

Here’s pseudocode for what I’m looking to do:

$('div1').fadeOut('slow' { delay_next_function_until_done: true } );
$('div2').fadeOut('slow' { delay_next_function_until_done: true } );
$('div3').animate({ top: 500 }, 1000 );

How do I achieve this?

Answer by Julian Aubourg

If you’re using a recent version of jQuery, use the animation promises:

$('div1').fadeOut('slow' ) ).promise().pipe(function() {
    return $('div2').fadeOut('slow' );
}).pipe(function() {
    return $('div3').animate({ top: 500 }, 1000 );
});

You can make it generic:

$.chain = function() {
    var promise = $.Deferred().resolve().promise();
    jQuery.each( arguments, function() {
        promise = promise.pipe( this );
    });
    return promise;
};

var animations = $.chain(function() {
    return $('div1').fadeOut('slow');
}, function() {
    return $('div2').fadeOut('slow');
}, function() {
    return $('div3').animate({ top: 500 }, 1000 );
});

$.when( animations ).done(function() {
    // ALL ANIMATIONS HAVE BEEN DONE IN SEQUENCE
});

Still a lot of function closures but that’s the very nature of Javascript. However, it’s much more natural and a lot more flexible using Deferreds/Promises since you avoid callbacks “inception”.

Answer by Starx

Callback is a friend, dont push it away. There are ways to simplify them. Here is one of them

$('div1').fadeOut('slow', div2)
function div3() { $('div3').fadeOut('slow'); }
function div2() { $('div2').fadeOut('slow', div3); }
March 25, 2012

PrettyPhoto – passing unique parameter from iframe window to parent page

Question by Gublooo

I’ve recently started using prettyphoto to display a video.

This is my current setup

<link rel="stylesheet" href="/css/prettyPhoto.css" type="text/css" media="screen" charset="utf-8" />
<script src="/js/jquery.prettyPhoto2.js" type="text/javascript" charset="utf-8">

<script type="text/javascript" charset="utf-8">
  $(document).ready(function(){
    var lastClicked = null;
    $("a[rel^='prettyPhoto']").prettyPhoto({
    callback: function()
    {           
        if(lastClicked != null) {
                var topicid = lastClicked.data("topicid"); 
                $.post('/course/close-video', {topic_id: topicid });
                lastClicked = null;
        }
    }
    }).click(function (){
        lastClicked = $(this);
});
</script>


<a data-topicid="<?php echo $topic->topic_id;?>" href="/course/play-video/topic_id/<?php echo $topic->topic_id;?>?iframe=true&width=470&height=340" rel="prettyPhoto" title="<?php echo $topic->topic_name;?>">
<img src="/images/videos/<?php echo $image_name;?>" width="170" height="103" alt="<?php echo $topic->topic_name;?>"/>
</a>

This is what is happening

1) When a user clicks on the link – the play-video php action is called which retrives the video url from database and passes so that it can be played on the popup window. This works fine.

2) Now the play-video also generates a unique id which is passed on to the page (iframe window) where the video is played. Right now I’m just displaying that value on the page. I can have that unique id stored as a hidden field or as a div value.

3) Now when the user closes this window – how can I access this unique id in the callback function of pretty photo which is in the main page.

Thanks a lot
Appreciate your time

Answer by Starx

Create a variable on the main page.

var UniqueId; //Unique ID that will arrive from the iframe

Now a function to write the value

function setUniqeId(val) {
    UniqueId = val;
}

Now inside the iframe, where the id, has already been receive, pass it to the parent like

parent.setUniqueId(TheIdThatIHaveReceived);

Update:

Make sure the script to read is after the DOM is loaded. One of the early ways of ensuring this, is placing the script after the Elements

<input id="topic_user_id" type="text" />
<script>
var unique_id = document.getElementById("topic_user_id").value;
parent.setUniqueId(unique_id);
</script>

One of the modern techniques would be to create a event handler like

window.onload = function() {
    var unique_id = document.getElementById("topic_user_id").value;
    parent.setUniqueId(unique_id);
};
...

Please fill the form - I will response as fast as I can!