FBJS

From Facebook Developers Wiki

Jump to: navigation, search

FBJS is Facebook's solution for developers who want to use JavaScript in their Facebook applications. We built FBJS to empower developers with all the functionality they need, and to protect our users' privacy at the same time.

Contents

How It Works

Most providers who allow developers to embed JavaScript within their domain force developers to use iframes to sandbox their code. Facebook has taken a different approach to this problem. JavaScript that you give us gets parsed, and any identifiers (function and variable names) get prepended with your application ID. For example, the following code block:

function foo(bar) { var obj = {property: bar}; return obj.property; }

becomes:

function a12345_foo(a12345_bar) { var a12345_obj = {property: a12345_bar}; return a12345_obj.property; }

This creates a virtual scope for every application that runs within Facebook. From there we expose certain functionality through a collection of JavaScript objects that allow you to modify your content on Facebook. Our objects are made to mimic the functionality of JavaScript as closely as possible, but it may take some getting used to for people who are already adept with JavaScript.

The Basics

The JavaScript syntax you've come to know and love (or hate) is exactly the same. You can create objects, use anonymous functions, create timeouts and almost any other thing you can think of. Modifying the DOM tree is slightly different, however.

Take this example FBML code, for instance:

<a href="#" onclick="hello_world(this); return false;">Hello World!</a> <script> <!-- function random_int(lo, hi) { return Math.floor((Math.random() * (hi - lo)) + lo); } function hello_world(obj) { var r = random_int(0, 255), b = random_int(0, 255), g = random_int(0, 255); var color = r+', '+g+', '+b; obj.setStyle('color', 'rgb('+color+')'); } //--> </script>

As you can see, creating FBJS is very similar to JavaScript. Note, however, that this example may not work as expected:

<a href="#" id="hello">Hello World!</a> <script> <!-- function random_int(lo, hi) { return Math.floor((Math.random() * (hi - lo)) + lo); } function hello_world(obj) { var r = random_int(0, 255), b = random_int(0, 255), g = random_int(0, 255); var color = r+', '+g+', '+b; obj.setStyle('color', 'rgb('+color+')'); } hello_world(document.getElementById('hello')); //--> </script>

In profile boxes, inline scripts are deferred until the first "active" event is triggered by a user. An active event is considered either onfocus, onclick, onmousedown, and so forth. Basically anything that requires a mouse click is an "active" event. On a canvas page, however, this example works just fine.

Also, please note it's very important that you use the syntax in the example above with your <script> tag hugging HTML comments. Otherwise <'s will be stripped out which makes coding very difficult ;). In the future we plan to modify our FBML parser to accept FBJS code without HTML comment wrappers, but for now it's required. -- Seems this is no longer true and you should be fine without them (though it doesn't hurt to include them and they'll be removed by the FBJS parser anyway).

FBJS DOM Objects

Retrieving Objects

A handle to an FBJS DOM object can be retrieved by either calling document.getElementById, or document.createElement. Additionally, the "this" pointer in DOM events also points to the target of the event.

Manipulating Objects

FBJS DOM objects implement most of the same methods regular JavaScript objects implement including: appendChild, insertBefore, removeChild, and cloneNode. Properties like parentNode, nextSibling, src, href (and many many others) have been redefined as a couplet of getters and setters.

Instead of obj.parentNode just call obj.getParentNode(), and so on. Most of the properties are easy to figure out, but here's an exhaustive list of properties in JavaScript and how they translate to FBJS:

JavaScript FBJS getter FBJS setter Description
parentNode getParentNode
nextSibling getNextSibling
previousSibling getPreviousSibling
firstChild getFirstChild
lastChild getLastChild
childNodes getChildNodes Returns a snapshot array of childNodes
innerHTML n/a setInnerFBML Note that this can throw an error if you pass a string directly. Use Fb:js-string to create the string first then pass that variable.
innerHTML n/a setInnerXHTML Beta feature. Allows you to set the innerHTML of an element by passing in a string of XHTML. The XHTML is sanitized according to FBML rules and then placed into the document.
innerText/textContent n/a setTextValue Not exactly like setInnerFBML as this will only allow text (no HTML)! It will remove all childNodes of the element it is called on.
form getForm Doesn't work, use document.getElementById('formid') instead
action getAction setAction
value getValue setValue
href getHref setHref
target getTarget setTarget
src getSrc setSrc
className getClassName setClassName
tagName getTagName
id getId setId
dir getDir setDir
checked getChecked setChecked
clientWidth getClientWidth
clientHeight getClientHeight
offsetWidth getOffsetWidth
offsetHeight getOffsetHeight
n\a getAbsoluteTop Returns the elements absolute position relative to the top of the page. Useful because of lack of offsetParent support.
n\a getAbsoluteLeft Same as getAbsoluteTop, but horizontally.
scrollTop getScrollTop setScrollTop
scrollLeft getScrollLeft setScrollLeft
scrollHeight getScrollHeight
scrollWidth getScrollWidth
tabIndex getTabIndex setTabIndex
title getTitle setTitle
name getName setName
cols getCols setCols
rows getRows setRows
accessKey getAccessKey setAccessKey
disabled getDisabled setDisabled
readOnly getReadOnly setReadOnly
type getType setType
selectedIndex getSelectedIndex setSelectedIndex
selected getSelected setSelected
location n/a setLocation
style getStyle setStyle
n/a getRootElement used as document.getRootElement - returns the top-level element of your profile box or canvas page

Manipulating Styles

Styles are set with the setStyle method and queried with the getStyle method. setStyle can set multiple styles using the syntax:

obj.setStyle({color: 'black', background: 'white'});

Or one style at a time using:

obj.setStyle('color', 'black');

Beware you need to camelize style names. This works:

obj.setStyle('textDecoration', 'underline')

But this won't:

obj.setStyle('text-decoration', 'underline')

You must also remember to use 'px' notation when referring to positions or height/width, and so forth.

This works:

obj.setStyle('width', '340px')

But this doesn't:

obj.setStyle('width', '340')

This is important to remember when you're using algorithms to calculate those values. You can't just use the calculated variable x like: setStyle('left', x), but rather like setStyle('left', x+'px').

Additional functionality for manipulating CSS classes has been added to FBJS DOM nodes.

addClassName(className) 
Adds a class name to the className string if it isn't already present.
removeClassName(className) 
Removes a class name from the className string if it present.
toggleClassName(className) 
If a class name exists, it removes it. If it doesn't exist it adds it.
hasClassName(className) 
Returns true if the class name exists or false otherwise.

Setting Content

innerHTML isn't implemented for security reasons. Three alternatives exist.

  1. obj.setTextValue(newText) can be used to set a literal text value inside of your DOM object (no HTML or FBML accepted).
  2. obj.setInnerFBML(fbJsStringVar) can be used to put HTML or FBML inside of your DOM object. Note that you need to create a Fb:js-string object first and pass it in as passing a string literal will result in an error.
  3. obj.setInnerXHTML(string) is a beta feature that allows you to place a string of XHTML directly into the document. The XHTML is sanitized in JavaScript before being rendered.

Working with Text Fields

Textbox selections have been implemented with the methods getSelection and setSelection. getSelection returns an object with properties start and end which correspond to the W3C-style attributes selectionStart and selectionEnd. setSelection takes two arguments, start and end (optional). This abstraction was added because Internet Explorer does not support selectionStart and selectionEnd. Since it is quicker in IE to retrieve both values together, they were coupled together into a single getter and setter. This function should work the same in all browsers with no extra work from you.

Creating FBML Elements

You can also use createElement to create FBML elements, although this is currently limited to fb:swf. Once it's created, it works just like any other DOM object does, however, once it is attached to the DOM you cannot move it and obj.getElementsByTagName('fb:swf') does not work.

var newSwf = document.createElement('fb:swf');

Events

Events can be added to FBJS DOM objects using the W3C-style addEventListener method. The third parameter, useCapture, is not supported. removeEventListener is also supported. In addition to the W3C event methods, we've also added listEventListeners and purgeEventListeners.

listEventListeners(eventName) 
Returns an array of handles of all events that have been added to this event. Events that were added in FBML using the on<event> attributes will also be included
purgeEventListeners(eventName) 
Removes all event listeners for a given event. This also removes events that were added as attributes in FBML.

Event handlers are called with one parameter, which is an object with information about the event. In the case of event handlers added as attributes, this object will be accessible through the "event" variable (just as it is in regular JavaScript). The event will have attributes target, type, pageX, pageY, ctrlKey, keyCode, metaKey, and shiftKey. It also implements two methods:

stopPropagation 
Prevents this event from propagating to any more elements further up in the DOM.
preventDefault 
Cancels the default behavior of this event without stopping propagation. For instance, preventDefault on an onfocus event will prevent that element from getting focus.
getId on event object of a listener function

When using the getId() method inside an event listener function on the event object, the following syntax may be used to retrieve the ID of the object that fired the event:

<div id="firedByDescription"></div> <div id="foo"></div> <div id="bar"></div> <script> //disclaimer: sample code block meant only to demonstrate functionality function myEventHandler(evt) { //we'll use this div later to drop stuff into it firedByDescription = document.getElementById('firedByDescription'); if (evt.type == 'mouseout') { //if the event is a mouseout, empty out the description div, and exit the event listener firedByDescription.setTextValue(''); return true; } //otherwise... do some processing: //*VERY IMPORTANT*: note that the object, which fired the event is located two nodes up in the DOM tree //See note below //eventFiredBy_ObjectId = evt.target.getParentNode().getParentNode().getId(); //On newer versions, it seems that there is no need to go up two levels int he DOM tree, hence eventFiredBy_ObjectId = evt.target.getId(); //works, whereas the first does not! //**NOTE** My testing of this suggests that when you call addEventListener() it adds it to the element, AND all it's descendants // This can then cause the event to be fired multiple times, as it is fired for the element and it's descendant elements. // When fired by a descendant element, you will probably have to do some kind of getParent()-ing // I'm raising this as a bug, as it does make things a little unworkable! //once you have the ID, you may, for example, drop its id into the firedByDescription div: firedByDescription.setTextValue(eventFiredBy_ObjectId); //... or do some conditional processing: if (eventFiredBy_ObjectId == 'foo') { //do something if the event was fired by 'foo' } else { //do something if the event was fired by 'bar' } } //add event listener to 'foo' div (mouseover & mouseout) document.getElementById('foo').addEventListener('mouseover',myEventHandler); document.getElementById('foo').addEventListener('mouseout',myEventHandler); //add *the same* event listener to 'bar' div (mouseover & mouseout) document.getElementById('bar').addEventListener('mouseover',myEventHandler); document.getElementById('bar').addEventListener('mouseout',myEventHandler); </script>

This functionality is very useful in cases where you have one event handler for multiple objects of the same type. Take for instance a shopping cart of some sort, or any type of object browser. When a user moves her mouse over one of the items in the cart, you may want to conditionally display information about the item -- by finding its ID, you may associate a description text for that item and display it to the user in another div. As you can see, using event listeners can be a very powerful way to display useful additional information to the user, based on where they move their mouse within your Facebook application. Happy coding and creativity!

AJAX

FBJS supplies a very powerful AJAX object for developers. Facebook proxies all AJAX requests and optionally runs useful post-processing on the data returned, such as JSON, or FBML parsing. To use it, just instantiate a new AJAX class. It supports the following properties:

ondone(data) 
An event handler which fires when an AJAX call returns. Depending on .responseType, data is an object, a raw string, or an FBML string.
onerror 
An event handler that fires when an error occurs during an AJAX call.
requireLogin 
If you set this to true the AJAX call will require the user to be logged into your application before the AJAX call will go through. The AJAX call will then be made with the regular fb_sig parameters containing the user's identity. If they refuse to login, the AJAX call will fail.
responseType 
This can be one of Ajax.RAW, Ajax.JSON, or Ajax.FBML.
useLocalProxy 
Beta. If this is true and you are using RAW or JSON type, the Ajax object will attempt to use fb:local-proxy to make a direct call to your app server. See FBJS_LocalProxy for more details.
Ajax.RAW 
The response from your server is returned to your callback in its original form.
Ajax.JSON 
The response from your server is parsed as a JSON object and returned to your callback in the form of an object. Properties of your JSON object that are prefixed with "fbml_" are parsed as individual FBML strings and returned as FBML blocks. These blocks can be used on a DOM object with the setInnerFBML method. Each variable and its value in the response is limited to a combined length of 5000 characters. Note: be sure to use json_encode or else you may see odd results with large data sets. See Bugzilla #363 for more information. json_encode is available by default in most PHP5 installations, and implementations for many other languages are available at json.org.
Ajax.FBML 
The response from your server is parsed as FBML and returned as an FBML block. This block can used on a DOM object with the setInnerFBML method.

And two methods:

post(url, query) 
Start an AJAX post. url must be a remote address, and query can be either a string or an object that is automatically converted to a string.
abort() 
Aborts an AJAX post.


Here's an example showing most of the functionality of AJAX: Ajax Example

Dialogs

Dialog is an object we've created to allow you to hook into our base dialog abstractions. It allows you to create rich and fully dynamic dialogs for your application.

Dialog(type) 
(constructor) type can be either Dialog.DIALOG_POP or Dialog.DIALOG_CONTEXTUAL.
Dialog.DIALOG_POP 
This is the type of dialog that shows up when you delete a wall post.
Dialog.DIALOG_CONTEXTUAL 
This is type of dialog that shows up when you delete a minifeed story.
onconfirm 
An event handler that fires when the user selects the button designed as "confirm" (left most button). If this event doesn't return false the dialog will be hidden.
oncancel 
An event handler that fires when the user selects the button designed as "cancel" (right most button). If this event doesn't return false the dialog will be hidden.
setStyle 
Allows you to set the style of the parent dialog node
showMessage(title, content, button_confirm = 'Okay') 
Displays a dialog with only a confirm button. title and content can be either strings or pre-rendered FBML blocks.
showChoice(title, content, button_confirm = 'Okay', button_cancel = 'Cancel') 
Displays a dialog with Confirm and Cancel buttons. title and content can be either strings or pre-rendered FBML blocks.
setContext 
(only applicable for DIALOG_CONTEXTUAL). Sets the context of a dialog, which basically means where the cursor arrow is pointing.
hide 
Hides this dialog if it is visible.

FBML Blocks

Blocks of pre-rendered FBML can be exported into your JavaScript scope on page load. To do this, simply wrap a block of FBML inside an <fb:js-string var="variable_name"> tag (see Fb:js-string for more information). Instead of rendering the block of FBML on the page it is put into a FBML block variable, which you can then use in your JavaScript with setInnerFBML. This is useful, because tags like <fb:swf> get rendered without waitforclick restrictions. FBML blocks can also be retrieved from AJAX calls, as explained above.

Animation

Facebook has provided a powerful animation library in FBJS. See Animation for more details.

Examples

Tips

  • Don't create JavaScript which depends on a sensitive DOM structure. Code like this.getElementByTagName('div')[1].getFirstChild().getLastChild().setStyle('color', 'white') is very fragile and may randomly break if we change the way certain elements are rendered.
  • Most FBJS DOM methods are chainable. For instance, instead of:
var obj = document.createElement('div'); obj.addEventListener('click', click); obj.addEventListener('mousemove', mousemove); obj.setStyle('color', 'black');
You can do:
document.createElement('div').addEventListener('click', on_click).addEventListener('mousemove', mousemove).setStyle('color', 'black');
  • You aren't allowed to extend base objects like Function or Array, however we do provide a typical "bind" implementation on the Function prototype.
  • FBJS objects don't contain handles to any of their actual DOM objects, however if you use Firebug, the console can show you exactly to what an object is referring. Try console.dir on an FBJS DOM object. In your console you'll see a PRIV_obj attribute which is the actual DOM node represented by your FBJS DOM handle. This can help you figure out what FBJS is doing behind the curtains. This trick also works with all other FBJS objects such as AJAX and FBML blocks.
  • Use Firebug to troubleshoot and diagnose anything that isn't working with your FBJS.
  • Consider using the Include files support to save processing/load times and bandwidth
Navigation