Protecting Usability in The Age of Like
As the web continues to evolve, the popularity of little widgets to display on your web pages -- Facebook "Like" buttons, "Tweet This" buttons, and the like -- are growing more popular. Like it or not, you may be asked to implement one (or a dozen) of them one day. This introduces potential points of failure, as each external resource could potentially go offline at any time for any duration. Nothing drives home the idea that nobody is immune more than the fact that the behemoth social network Facebook has unexpectedly gone offline twice in the last two weeks. How do you protect your websites from additional stress in the form of delayed page loading when an event like this occurs?
With careful planning.
The first thing we need to do is understand the problem. I will use the Facebook "Like" button because I'm familiar with it, but the concept applies to nearly anything you include directly from another website.
The problem
When you include a <script> block in the middle of your <body> block, the browser will stop rendering the DOM at that point, download the script file, execute it, and then continue. This is referred to as Blocking (of page rendering), and is the most severe example. This is why conventional wisdom says constantly reiterates that you should place <script> blocks as the very last thing inside the <body> block -- so that if the script source is unavailable or slow, the rest of your page is not waiting on it.
Facebook "Like" buttons, fortunately, don't require a <script> block to embed themselves on your pages. Instead, they use <script>'s slightly less evil cousin, iFrame. IFrames do not block page rendering; which is to say that they are rendered in parallel with everything else on the page. However, they still present two problems. First, they share their connection pool with the parent page, and second, and probably more importantly, they do block the page's onLoad event.
The onLoad event is what a majority of JavaScript frameworks recommend waiting for to initialize your applications (rightfully so). What this means is that, if Facebook were to go down, and you have 1 "Like" button on the page, the browser's downloading/busy indicator won't stop until the request to Facebook for the iFrame content times out -- the duration of which depends on the user's browser -- and then, and only then, will the onLoad event fire. If you are unlucky enough to have multiple "Like" buttons on a page, then they each must timeout before onLoad will fire. Depending on the number of buttons you're using, they could either all be executing in parallel, or if you've got more buttons than threads in the connection pool, then you'll have some queued up, effectively doubling (or more!) the already seemingly-eternal wait.
If your website isn't of much use without the JavaScript that runs when the onLoad event fires, then chances are good that users aren't going to wait around for 30 seconds to a minute. I know I wouldn't -- I give up on even the most interesting link after what feels like 5 seconds.
The solution
Not to fret, there is a simple and elegant way to add Facebook "Like" buttons (and their ilk) after the page has completed loading, which will provide your users with the best experience, especially when the external sites you depend on end up on a milk carton.
My suggestion is to start with a placeholder that contains all of the information you need to build the iFrame.
<a class="fbLike" href="{iFrame url}"></a>
If, instead of adding an iFrame, you use the same URL in an Anchor (<a></a>) tag, then the page will continue to load as normal, and the user will see nothing. I've added a class to make identifying all of the placeholders for my FB Like buttons simple.
Now, after the page has loaded and the onLoad event fires, we want to replace our placeholders with iFrames to inject the buttons. I'll do this with a few lines of jQuery:
$(function(){
$(".fbLike").each(function(){
var t = $(this);
var info = t.attr("href");
t.replaceWith("<iframe src='" + info + "'></iframe>");
});
});
Note that I've left off some of the iFrame tag attributes for the sake of keeping the code sample short and sweet; just add them back as needed.
What this does is iterate over every placeholder on the page and replace it with an iFrame, using the same URL that the placeholder anchor tag had been using. I'm not sure if this will restart the browser's busy indicator, but at least the rest of your website will be usable.
Bonus
Since you're setting a placeholder, one nice usability improvement would be to style that placeholder to indicate to users that your FB Like button is loading. Perhaps give it a background image of the Like button grayed out, and a tooltip of "Loading Like button from Facebook..."
Posted in Best Practices | Facebook | JavaScript | jQuery | 8 Responses October 06 2010
