One of the challenges at TripAdvisor is working internationally -- both for non-US members and US members who are travelling. We work hard to make sure that our site works from everywhere.
Recently, we began rolling Facebook Connect out on the site as a login mechanism. For us, Connect is a great way to streamline the login process and make it even easier for existing members to login and for new members to sign up. To get started, Facebook has provided some excellent documentation.
The problem is that the suggested approach -- including the FeatureLoader javascript right after the body tag can cause serious problems when it is unavailable. The important thing to realize is that many things can cause this file to become unavailable: being blocked in a workplace, downtime of Facebook's servers, or even by a national policy (as in China). As a service that our members have come to rely on, TripAdvisor must continue to work in light of those sorts of issues.
Failure Modes
There are two big failure modes to worry about: the file itself not being available and the browser not being able to find the Facebook servers at all.
File Is Not Available
The good news with this failure mode is that even if the file is not available, the page can continue to load: our members can find review, book hotels and so on. Naturally, functions that rely on Facebook classes won’t work and we'll want to turn them off. A simple approach is to set a javascript var to say that Facebooks' libraries have initialized correctly; successive functions can check on that:
FB_RequireFeatures(["XFBML", "Api", "Connect"], function(){
FB.Facebook.init(apiKey, '/xd_receiver.htm');
FB.ensureInit(function(){
// Facebook JS is now ready for use
window.FacebookInitialized = true;
});
FB.XFBML.Host.get_areElementsReady().waitUntilReady(function(){
// FBML has been processed
window.FBML_initialized = true;
});
});Can’t Find Server
Not finding a server is a more serious problem. The reason is that the web browser could block waiting for a timeout. This will result in it not loading the rest of the page: no reviews, no bookings, for all intents and purposes, no TripAdvisor. This is a Very Bad Thing. Even worse, it is how many firewalls are designed.
Solutions
In order to solve these concerns, we need to make sure Facebook is available, while making sure that the page loads correctly when it is not. We went through a couple of iterations to get it right.
Iframe With Callback
One approach we've tried is to use an iframe with a callback. We create a hidden iframe that tries to load the Facebook javascript as they recommend and then call a method on the parent saying it loaded. The problem is that this causes the browser to appear as though it is waiting for something to finish, even though the main page is fully loaded. This is confusing to the user and not ideal.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:fb="http://www.facebook.com/2008/fbml">
<head>
<script type="text/javascript" src="http://static.ak.connect.facebook.com/js/api_lib/v0.4/FeatureLoader.js.php">
</script>
</head>
<body>
</body>
</html>
Iframe With Script Injection
Next, we tried to using the iframe with JavaScript injection. For the moment, ignore that the FeatureLoader JS does a document.write under certain circumstances. We wait for the page to load and then inject a script tag in the page head. This lets the browser look like it is done, which as far as the user is concerned, it is.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" xmlns:fb="http://www.facebook.com/2008/fbml">
<head>
<script type="text/javascript">
function loadFacebook() {
// load Facebook API
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = 'http://static.ak.connect.facebook.com/js/api_lib/v0.4/FeatureLoader.js.php';
document.getElementsByTagName("head")[0].appendChild(script);
}
</script>
</head>
<body onload="loadFacebook()">
</body>
</html>
The problem here is that we don't know when the JS is done loading in the iframe.
Iframe With Script Injection After Callback
Finally, we took the approach of injecting the JavaScript and then waiting on a callback to determine when it is available. For this, we use some trivial functions to check if any of the variables set by Facebook’s JavaScript are available. If they are, we're done.
When the JavaScript is available we call to the parent, which can do the same injection to pull in the JS (which the browser will have cached!). Now the parent can run functions in Facebook's libraries.
In either case, we remove the iframe; it is unnecessary at this point. If we did timeout, removing the iframe will force a timeout on the load script.
<script type="text/javascript">
// called on load
function loadFacebook()
{
// load Facebook API
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = 'http://static.ak.connect.facebook.com/js/api_lib/v0.4/FeatureLoader.js.php';
document.getElementsByTagName("head")[0].appendChild(script);
script.onload = facebookLoaded;
script.onreadystatechange = function()
{
if (this.readyState == 'loaded' || this.readyState == 'complete') facebookLoaded();
}
}
function facebookLoaded()
{
FB_RequireFeatures(["XFBML", "Api", "Connect"], function(){
FB.Facebook.init(apiKey, '/xd_receiver.htm',
{
doNotUseCachedConnectState: true,
permsToRequestOnConnect: "email"
}
);
FB.ensureInit(function(){
// Facebook JS is now ready for use
window.FacebookInitialized = true;
});
FB.XFBML.Host.get_areElementsReady().waitUntilReady(function(){
// FBML has been processed
window.FBML_initialized = true;
});
});
}
</script>

0 comments:
Post a Comment