FBJS
出自YiqiWiki
FBJS是开发者在自己的fb应用中使用的js的方案。FBJS是为了让开发者使用他们需要的js功能并且保护我们平台中的用户的隐私。
目录 |
FBJS是怎样工作的
大多数允许嵌入js代码的提供商要求开发者使用iframe的方式来保护他们的代码。 Facebook采用了不同的解决方案。 开发者提供的js会被分析,并且所有标识符(函数和变量名)会加上一个应用id的前缀。 例如,下面的代码片断:
function foo(bar) {
var obj = {property: bar};
return obj.property;
}
被转换成:
function a12345_foo(a12345_bar) {
var a12345_obj = {property: a12345_bar};
return a12345_obj.property;
}
这样就为每个f8应用创建了一个虚拟生命周期。我们还提供了一系列js对象,用来修改你的f8应用的内容。这些对象尽可能地模仿原来js中对应对象的功能,但是对于那些已经熟悉了js的人也需要花点时间才能掌握。
基础知识
FBJS跟你已经熟悉的js语法完全一样.你可以创建对象,使用匿名函数, 创建timeout 等其他你想做的事情. 但是,修改dom树的方法稍微有点不同。
例如:下面的FBML代码:
<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>
正如你看到的,创建FBJS代码与标准的js非常相似。 但是,这段代码并不像你期望的那样工作:
<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>
在profile页中, 内联script会被用户第一次操作触发。操作包括 onfocus, onclick, onmousedown等等。 基本上所有用户的点击都是操作。但是canvas页中,上述代码也可以正常工作。
请注意,在script标签中要像上面例子那样使用html注释语法, 否则 ‘<’会被删除,从而编写正确代码变得困难。将来,我们计划修改我们的FBML解析器,以支持不需要上述注释方式的js代码,但是现在还不可以。
FBJS DOM 对象
Retrieving 对象
一个FBJS DOM对象可以通过 document.getElementById或者 document.createElement来调用。除此之外,DOM中的this指针也可以。
Manipulating 对象
FBJS DOM 对象实现了许多跟常用的js对象一样的接口方法,包括: appendChild, insertBefore, removeChild, and cloneNode。许多parentNode, nextSibling, src, href 等等这样的属性已经被重新定义为一对get和set方法.
例如应该象这样调用 obj.getParentNode(),而不是原来的obj.parentNode。 下面是js的属性转化成 FBJS的详细列表:
| JavaScript | FBJS getter | FBJS setter | Description | |
|---|---|---|---|---|
| parentNode | getParentNode | |||
| nextSibling | getNextSibling | |||
| previousSibling | getPreviousSibling | |||
| firstChild | getFirstChild | |||
| lastChild | getLastChild | |||
| childNodes | getChildNodes | 返回子节点数组。 | ||
| innerHTML | n/a | setInnerFBML | 注意:如果你直接传入一个字符串,会抛出异常。应该使用 [Fb:js-string] 来创建一个字符串,然后再传入。 | |
| innerHTML | n/a | setInnerXHTML | Beta feature. 允许你传入一个XHTML字符串来设置一个元素的innerHTML。这个XHTML按照FBML的规则清洗了一次,然后放进document中。 | |
| innerText/textContent | n/a | setTextValue | 不完全象 setInnerFBML 的话,只允许为TEXT (而不是 HTML)! 被调用的节点的所有的子结点将被删除。 | |
| form | getForm | 不工作, 用 document.getElementById('formid') 代替 | ||
| 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 | 返回相对于页顶的相对位置。 因为缺乏 offsetParent 支持而变得非常有用. | ||
| n\a | getAbsoluteLeft | 与getAbsoluteTop一样,但是是横向的。 | ||
| 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 | 象 document.getRootElement 这样使用- 返回你的profile 页或者canvas 页的顶层元素。 |
操作样式
Styles are set with the setStyle method and queried with the getStyle method. 调用setStyle方法来设置styles,调用getStyle来请求styles。setStyle 能够按照下面的语法设置多个style:
obj.setStyle({color: 'black', background: 'white'});
或者同时使用一个style:
obj.setStyle('color', 'black');
注意,你需要style名称来设置属性时, 象下面这样:
obj.setStyle('textDecoration', 'underline')
但是,下面这样就不会起作用:
obj.setStyle('text-decoration', 'underline')
当你指定位置或者高度/宽度等等时,必须记住使用‘px’符号。
下面能正常工作:
obj.setStyle('width', '340px')
但是下面的不能:
obj.setStyle('width', '340')
当你用算法来计算他们的值时,这是非常重要的。你不能象下面这样计算x的值:
setStyle('left', x)
但是这样可以:|setStyle('left', x+'px')
除此之外,操作css的方法也加在了FBJS DOM节点类的方法中。
- addClassName(className)
- 增加一个类名到classname字符串,如果这个字符串不是已经存在的。
- removeClassName(className)
- 从className字符串中删除一个类名,如果这个字符串不是已经存在的。
- toggleClassName(className)
- 如果这个类名存在,则删除,否则,就增加.
- hasClassName(className)
- 如果类名存在返回true,否则为false。
设置内容
innerHTML 没有实现是出于安全的考虑。 有两种替代方法。
- obj.setTextValue(newText) 用于设置你的DOM对象里的一个text的值(不支持html或者fbml)。
- obj.setInnerFBML(fbJsStringVar) 用于把html或者fbml放进你的DOM对象中。你需要首先创建一个Fb:js-string对象然后传入,如果你直接传送字符串,会出错。
文本框的使用
文本框选项用getSelection 和 setSelection实现。 getSelection 返回一个对象,该对象包含start 和 end 属性,对应W3C风格的属性selectionstart和selectionend。 setSelection 有两个参数, start 和 end(可选)。 增加这两个抽象是因为ie浏览器不支持selectionStart 和 selectionEnd。因为在ie中同时查询俩个值是非常快的, 所以在getter和setter中俩个属性同时出现。 这个函数在所有的浏览器中运行方式都一样,对于你来说没有任何额外的工作。
Events(事件)
“事件”可以添加到采用W3C-style(样式) addEventListener method的FBJS DOM 对象中去,仍支持removeEventListener,但不支持第三个参数—useCapture。除了W3C“事件”方法(W3C event method)之外,我们还增加了listEventListeners和purgeEventListeners2种方法:
- listEventListeners(eventName)
- 对任何事件,均可返回一组该事件的处理结果。其中也包括采用on<event>属性的FBML中添加的事件。
- purgeEventListeners(eventName)
- 可一个特定事件移除所有listeners,还可以移除那些作为属性添加到FBML里的事件。
人们通常把Event handlers(事件处理器)与一个参数联系在一起,该参数是个带有事件信息的对象。如果事件处理器被作为属性添加,该对象则可通过“事件”变量(如同它在常规JavaScript中一样)来获得。 事件将拥有下列属性:target, type, pageX, pageY, ctrlKey, keyCode, metaKey, 和 shiftKey。并且还可以使用下面2种方法:
- stopPropagation
- 防止该事件扩散到DOM中任何更多的元素里面。
- preventDefault
- 取消该事件的缺省动作而无需停止扩散。例如,在onfocus 事件中用preventDefault 就可以阻止(事件的)该元素获得焦点。
- getId on event object of a listener function
当在事件对象的Listener函数中使用getID方法时,下列语法将被用来获取(激活该事件的)对象的ID:
<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>
当你拥有一个同类型多对象的事件处理器时,该功能将非常有用。比如购物车,或任意类型的对象浏览器。当一个用户移动鼠标至购物车中的某件商品时,你会习惯性地通过它的ID展示该商品的相关信息,你也许还会为该商品添加一个描述文本,并把它展示给其他用户。正如你所看到的,基于在Facebook应用中移动鼠标的位置,采用事件listeners将是非常有效的方式来向用户展示更多有用信息。祝你们编码愉快,创作愉快!
AJAX
FBJS 为开发者支持一个非常强大的AJAX对象。Facebook代理所有的AJAX请求,并选择运行有用的返回数据后处理程序,比如 JSON或FBML句法。为了便于使用,我们举个新AJAX类别的例子。它支持下列属性:
- ondone(data)
- AJAX请求返回时激活的一个事件处理器,依赖于.responseType ,数据会是一个对象,一个原始字符串,或一个FBML串。
- onerror
- 当一个AJAX请求发生错误时激活的一个事件处理器。
- requireLogin
- 如果你认为AJAX请求确实会在通过之前,要求移植用户到你的应用中去的话,那么该AJAX请求将会被赋予常规的fb_sig 参数,用来包含用户身份。如果用户不愿登录,该AJAX请求将失败。
- responseType
- 这可以是一个Ajax.RAW, Ajax.JSON,或 Ajax.FBML.
- useLocalProxy
- Beta版。如果你确实正在使用RAW或JSON type, Ajax对象将趋于使用[fb:local-proxy] 来向你的APP服务器发出一个直接请求。看[FBJS_LocalProxy] 获取更多细节。
- Ajax.RAW
- 来自服务器的请求源码被返回到你的回应系统。
- Ajax.JSON
- 来自服务器的请求被解析成一个JSON对象,并被以对象形式返回到回应系统。字头带有“FBML”的JSON对象的属性被解析成独立的FBML串,并被作为FBML blocks返回。这些blocks可通过setInnerFBML方法,被用于DOM对象。请求中的每个变量和变量值都限于总共5000个字符长度。 Note: 确认使用json_encode,否则你会看到带大量数据集的奇怪结果。看Bugzilla #363 可获得更多信息。大多数PHP5设备都可获得缺省的json_encode,其他语言的可执行版本可在json.org获得。
- Ajax.FBML
- 来自服务器的请求被解析成FBML,并被作为FBML blocks返回。这些blocks可通过setInnerFBML方法,被用于DOM对象。
另2个方法:
- post(url, query)
- 启动一个AJAX post。url 必须是个远程地址,query 可以是一个字符串或者一个自动转换成字符串的对象。
- abort(放弃)
- 放弃一个AJAX post。
这儿有个例子可以显示AJAX的大多数功能:
[FBJS/Examples/Ajax |Ajax Example]
对话框
Dialog是一个基于我们对话框的类,它能让你在应用中创建丰满的动态对话框。
- Dialog(type)
- (构造) type 可以是 Dialog.DIALOG_POP 或者 Dialog.DIALOG_CONTEXTUAL.
- Dialog.DIALOG_POP
- 这种类型是当你删除a wall post的时候显示的对话框。
- Dialog.DIALOG_CONTEXTUAL
- 这种类型是当你在删除minifeed story的时候显示的对话框。
- onconfirm
- 是一个句柄,当用户选择标示为"确认"的按钮时调用,如果这个事件不返回false,这个对话框将被隐藏。
- oncancel
- 是一个句柄,当用户选择标示为"取消"的按钮时调用,如果这个事件不返回false,这个对话框将被隐藏。
- setStyle
- 设置父对话框节点的样式。
- showMessage(title, content, button_confirm = 'Okay')
- 展示一个只有“确认”按钮的对话框。title 和content 可以使字符串或者 预先提供的 FBML blocks。
- showChoice(title, content, button_confirm = 'Okay', button_cancel = 'Cancel')
- 展示一个带着“确认”和“取消”按钮的对话框。title和content可以使字符串或者 预先提供的 FBML blocks。
- setContext
- (仅仅适用于 DIALOG_CONTEXTUAL). 设置对话框的内容, 这个对话框显示鼠标正在指向的元素的说明。
- hide
- 隐藏可见的对话框
FBML Blocks
之前提供的FBML Blocks能够输出到你的页加载的JavaScript中。为此,简单地总结FBML Blocks代替为一段<fb:js-string var="variable_name"> 标签 (详细请看 Fb:js-string)。不是渲染FBML Blocks在网页上,而是放进FBML Blocks变量,然后,您可以在setinnerfbml使用您的JavaScript。这是非常有用的,因为象<fb:swf> 的标签获得所提供的不带waitforclick 的限制。FBML块也可以被Ajax提出请求,如上文所述。
动画
Facebook 提供了一个强大的动画库。 详细请看 Animation。
示例
- Hello World!
- Typeahead widget
- Ajax Typeahead widget
- Dialogs
- Dynamic Dialogs using Ajax
- Ajax
- Dynamic Tables
- Slider Widget
- Tabs
技巧
- 不要创建 依赖于敏感的dom结构的JavaScript。象代码
this.getElementByTagName('div')[1].getFirstChild().getLastChild().setStyle('color', 'white'),如果我们改变某些要素的方式被提供了,这些代码是很脆弱并可能被攻破。
- 大部分 FBJS DOM 方法是连续的,例如,而不是:
var obj = document.createElement('div');
obj.addEventListener('click', click);
obj.addEventListener('mousemove', mousemove);
obj.setStyle('color', 'black');
你可以这样做:
document.createElement('div').addEventListener('click', on_click).addEventListener('mousemove', mousemove).setStyle('color', 'black');
- 你不能像函数或阵列那样继承对象,不过,我们对函数的原型提供了一个典型的“bind”实现。
- FBJS 对象不包含任何他们实际的DOM对象的句柄,但是如果你用firebug,控制台可以告诉你句柄指向的实际对象。试试 console.dir 在 FBJS DOM 对象上。在您的控制台你会看见一段实际的DOM节点代表您的fbjs DOM的处理的属性PRIV_obj 。 这能帮助你清楚FBJS实际上在做什么。这个技巧对例如AJAX和FBML blocks等FBJS 对象都适用。
- 如果您想要使用 timed fading/unfading 的元素, 创建下拉阴影“panes”或一般拖拽, check out the small FBJS effects library Backface, at http://supercodex.com/backface/backface.zip, with a demo at http://apps.facebook.com/backface.
- 用 [Firebug]去排查和诊断任何那些不在你的FBJS上工作的事情 。
- 考虑使用[Include files]支持,以节省处理/加载时间和带宽。

