Recently I had set myself the task to write an interface for my Content Management System to publish content to either a personal page or a fanpage page on Facebook or how they call it: canvas. Assuming this wasn't so difficult I entered the developer namespace of Facebook, where I was unpleasently surprised by a couple of things.
It turns out Facebook has it's own set of JavaScript rules and regulations which it most cleaverly named FBJS. After using it I've come up with some important things you should know when developing with FBJS.
Before we start, let's ask ourselves:
What is FBJS?
FaceBook JavaScript is a JavaScript library which you can (hardly) compare to a library like jQuery. It can help you as a developer to make your pages more interactive with the use of DOM manpulation (event delegation, creating elements, ..), Proxy'd AJAX (to fetch content from your own server) and User Interface elements (dialog, modal).
When your application is loaded into a Facebook canvas, your FBJS is first parsed serverside by Facebook, prepending all your declared variables and functions with a namespace. Thus assuring them you will have no access to whatever JavaScript they themselves have on the page.
For example, when you write:
function foo (bar)
{
var obj = {property: bar};
return obj.property;
}
Facebook will parse this and turn it into:
function a12345_foo (a12345_bar)
{
var a12345_obj = {property: a12345_bar};
return a12345_obj.property;
}
The document variable
FBJS will even spoof the native document variable and replace it with it's own version providing you limit access to it's original and only enabling document.getElementById and document.createElement. That's it. Even the use of document.getElementsByTagName is not allowed!
The spoofed document variable is created to restrict your access in order to protect as well as Facebook and their visitor from misusage of JavaScript. In all fairness, I fully understand their reasons. You can imagine what kind of threats would arise if they would grant full JavaScript access to anyone.
However, these limitations will make life a living HELL for usas a developer. Because not only do they restrict the usage of the DOM. They also restrict you from using any JavaScript libraries such as jQuery or Dojo. Can you imagine doing simple animations? Traversing through the dom using a query method? These are two of the main features you'll miss when developing in FBJS and you'll have to do by hand.
DOM elements
The spoofed document variable offered by FBJS has a direct effect on DOM elements: these are spoofed as well. Here are a few important things you should know about DOM elements in FBJS.
As they are spoofed they won't shop up as normal elements in your console. Instead they are shown as Object {PRIV_obj}.
These DOM elements do not offer the attributes you're so used to in natie JavaScript. Instead they only offer methods of which you can see a list here.
DOM elements offer an addClassName, removeClassName, toggleClassName and hasClassName method.
The methods in DOM elements always return their respective element (unless metioned otherwhise) which means they are chainable. This means you can do div.addEventListener ('click', function () { }).addClassName ('listener1').addEventListener ('click', function () { }).addClassName ('listener2');
Event listeners/handlers, "waitforclick" and user interaction
FBJS supports event listeners in the same syntax as normal JavaScript, except the lack of the useCapture argument. Beside the syntax and lack of useCapture there is, however a big surprise waiting for those that will use them -- they won't get fired the first time.
Oke, for this to make sense, let me introduce you to another restriction Facebook has bound to the use of FBJS called waitforclick. You could call it a restriction, but it's actually an event listener bound to your canvas stating that any and all JavaScript will be stalled from execution untill the user either triggers a focus, click or mouseup event.
This means that all JavaScript - even that in self executing functions - will have to wait for this event to be triggered. Resulting the user having to click twice on elements to which you've bound a click event using the addEventListener method.
There is an upside: After the first waitforclick event all other pages you might link to that require the canvas to be reloaded, will not have this restriction. Note this does not include a complete refresh of the browsers window.
So, what's the workaround? There is one ways you can work around this.. and that's by using inline event listeners (e.g. <a href="" onClick="something (this);">).
Here's an example of a succesful splash fade to give you some insight in what you'd be working with:
<div id="splash" onClick="splash ();"></div>
function splash ()
{
var el = document.getElementById ('splash');
var duration = 1000;
var step = 1 / 100;
var current = 1;
(
function run ()
{
var temp = current - step;
current = temp > 0 ? temp : 0;
el.setStyle ('opacity', current);
if (current == 0)
document.getElementById ('content').removeChild (el);
else
setTimeout (run, 10);
}
)();
}
Unlike the addEventListener method, which relies on the JavaScript to be ran first. Inline attached events are directly attached to the DOM element regardless of JavaScript execution. Thus, the code above will execute regardless of the waitforclick event, because you've bound the click event directly to the element.
If you don't trust me.. I've tried a couple of work arounds which all failed to mislead the waitforclick restriction, like:
No external JavaScript
Using FBJS you cannot include external files. For big applications it's always useful to seperate your code in files so I suppose they're telling you to KISS (Keep it simple, stupid).
Limited to inline JavaScript tags, my advice (and my personal method) is to store your files externally, but use a serverside method to include them between the inline <script/> tags. This will allow you to keep things seperate and flexibile while developing, while obeying the Facebook restrictions.
Facebook documentation is not always right
When developing with FBJS you'll realize their documentation is sometimes a bit off and sometimes even their logic. The easiest example is the code they've used in their Unobtrusive example.
Their idea to use addEventListener is a clean and respectable approach, I use it all the time. However, when the user loads the page for the first time and there hasn't been a waitforclick event, the init function will not run untill that event happens. Resulting you to have to click twice before the hello_world function is run.
Another great thing is their use of document.getElementByTagName which for one is a typo. But even if correct the use of document.getElementsByTagName isn't allowed.
Conclusion
Facebook's FBJS isn't all that and could surely use some upgrading. I'm very keen on looking at what they'll be offering with the upcoming iframe implementation. Will it be the same as they support for applications or something entirely different? Who knows!
All I can say, I hope you've learned from this post and comments and/or questions are always appreciated.
* Note: A great help in developing will be #facebook on freenode.net. These are the guys that already have delt with the issues I've described in this post and you'll probably find me there also :)