Archive pour le mot-clef ‘facebook’

Callback for 3rd party javascript

Vendredi 6 juillet 2012

At PunchTab, we’re dealing with the same technical challenges as Facebook or Twitter: we’re providing a 3rd party Javascript snippet that publishers install on their website.

One of the features we have to provide is a callback that we or our publishers can use to execute code once the PunchTab Javascript has been loaded and executed.

The Facebook approach

Currently, we’re using the Facebook approach: if the publisher has defined a specific function in the global namespace, we execute it when we’re done with our own functions. Where Facebook use fbAsyncInit, we use ptAsyncInit. The publisher may define this function as follows:

window.ptAsyncInit = function(){alert('PunchTab is ready')};

And at the end of our javascript, we simply add:

if (window.ptAsyncInit !== undefined) {
    window.ptAsyncInit();
}

It’s convenient for basic usage cases, but not when multiple 3rd party libraries are involved.

Indeed, a publisher may load Facebook and PunchTab asynchronously. As we’re registering some callbacks for Facebook, we have to use fbAsyncInit, but we never know which will be loaded first – them or us. Here is the code we use to execute our code for Facebook:

var previous_fbAsyncInit = window.fbAsyncInit;
if ((window.fbAsyncInit && window.fbAsyncInit.hasRun) ||
    (!window.fbAsyncInit && window.FB !== undefined && FB.Event !== undefined)) {
    // execute our code relying on FB
} else {
    window.fbAsyncInit = function () {
        if (previous_fbAsyncInit) {
            previous_fbAsyncInit();
        }
        // execute our code relying on FB
    };
}

The issue here is that we have to first detect if Facebook is not already loaded, which is tricky. If it is already loaded, we just execute our code directly. If Facebook isn’t loaded, we redefine the window.fbAsyncInit function to call our code once Facebook is ready. You can notice that our fbAsyncInit is a monkey patch to execute the previous fbAsyncInit if it exists.

As you can see, the global callback is not convenient for two reasons:

  • It is difficult to have multiple callbacks
  • It is not made for the case when Facebook is loaded before another application

The Twitter approach

To achieve the same effect with Twitter, you have to use the snippet on this page. Instead of using a public callback, it defines the global twttr object directly in the snippet (if it doesn’t exist) and then adds a useful function to it: ready():

return window.twttr || (t = { _e: [], ready: function(f){ t._e.push(f) } });

You can then call twttr.ready(your_callback) as many times as you want. It will be pushed into a queue (_e) which will be executed when Twitter is loaded. If Twitter is already loaded, this overwrites the function to directly execute the callback (smart!) and it solves the two previous issues.

The future PunchTab approach

We’re currently rewriting our Javascript SDK, where we are going to switch to the Twitter approach – but in an even simpler way. Twitter does this in a slightly trickier way to avoid leaking in the global namespace. But we chose to use a global variable ptReady to achieve the same.

You will able to do the following:

window.ptReady = window.ptReady || [];

which defines a basic Javascript array if it doesn’t already exist. And then, you just add this:

ptReady.push(your_callback)

if PunchTab is not ready, the queue will be processed when we are. If we are already loaded, we redefine the push function to directly execute the callback. Kaboom!