So, my friend is resuming his movie-making projects (site in Italian) and I decided to spend some time to make the site better integrated with Facebook, since that is actually his main contact media (and for what he does, it’s a good tool). The basic integration was obviously adding the infamous “Like” button.
Now, adding a button should be easy, no? It’s almost immediate doing so with the equivalent Flattr button, so there should be no problem to add the Facebook version. Unfortunately the documentation tends to be self-referencing to the point of being mostly useless.
Reading around google, it seems like what you need is:
- add OpenGraph semantic data to the page; while the OpenGraph protocol itself only mandates title, url, type and image, there is an obscure Facebook doc actually shows that to work it needs one further data type,
fb:admins
; - since we’re adding
fb:
-prefixed fields, we’re going to declare the namespace when using XHTML; this is done by addingxmlns:fb="http://www.facebook.com/2008/fbml"
to the<html>
field; - at this point we have to add some HTML code to actually load the Javascript SDK… there are a number of ways to do so asynchronously… unfortunately they don’t rely, like Flattr, on loading after the rest of the page has loaded, but need to be loaded first, following the declaration of a
<div id="fb-root" />
that they use to store the Facebook data.
window.fbAsyncInit = function() {
FB.init({ cookie: true, xfbml: true, status: true });
};
(function() {
var e = document.createElement('script');
e.src = document.location.protocol + '//connect.facebook.net/en_US/all.js';
e.async = true;
document.getElementById('fb-root').appendChild(e);
}());
* then you can add a simple like button adding <fb:like/>
in your code! In theory.
In practise, the whole process didn’t work at all for me on the FSWS generated website, with neither Chromium nor Firefox; the documentation and forums don’t really give much help, most people seems to forget fields and so on, but for what I can tell, my page is written properly.
Trying to debug the official Javascript SDK is something I won’t wish for anybody to be forced to; especially since it’s minimised. Luckily, they have released the code under Apache License and is available on GitHub so I went on and checked out the code; the main problem seems to happen in this fragment of Javascript from connect-js/src/xfbml/xfbml.js
:
var xfbmlDoms = FB.XFBML._getDomElements(
dom,
tagInfo.xmlns,
tagInfo.localName
);
for (var i=0; i < xfbmlDoms.length; i++) {
count++;
FB.XFBML._processElement(xfbmlDoms[i], tagInfo, onTagDone);
}
For completeness, dom
is actually document.body
, tagInfo.xmlns
is "fb"
and tagInfo.localName
in this case should be "like"
. After the first call, xfbmlDoms
is still empty. D’uh! So what is the code of the FB.XFBML._getDomElements
function?
/**
* Get all the DOM elements present under a given node with a given tag name.
*
* @access private
* @param dom {DOMElement} the root DOM node
* @param xmlns {String} the XML namespace
* @param localName {String} the unqualified tag name
* @return {DOMElementCollection}
*/
_getDomElements: function(dom, xmlns, localName) {
// Different browsers behave slightly differently in handling tags
// with custom namespace.
var fullName = xmlns + ':' + localName;
switch (FB.Dom.getBrowserType()) {
case 'mozilla':
// Use document.body.namespaceURI as first parameter per
// suggestion by Firefox developers.
// See https://bugzilla.mozilla.org/show_bug.cgi?id=531662
return dom.getElementsByTagNameNS(document.body.namespaceURI, fullName);
case 'ie':
// accessing document.namespaces when the library is being loaded
// asynchronously can cause an error if the document is not yet ready
try {
var docNamespaces = document.namespaces;
if (docNamespaces && docNamespaces[xmlns]) {
return dom.getElementsByTagName(localName);
}
} catch(e) {
// introspection doesn't yield any identifiable information to scope
}
// It seems that developer tends to forget to declare the fb namespace
// in the HTML tag (xmlns:fb="http://www.facebook.com/2008/fbml") IE
// has a stricter implementation for custom tags. If namespace is
// missing, custom DOM dom does not appears to be fully functional. For
// example, setting innerHTML on it will fail.
//
// If a namespace is not declared, we can still find the element using
// GetElementssByTagName with namespace appended.
return dom.getElementsByTagName(fullName);
default:
return dom.getElementsByTagName(fullName);
}
},
So it tries to workaround when the xmlns is missing by using the full name fb:like
rather than looking for like
for their own namespace! So to work it around at first, I tried adding this code before FB.init
call:
FB.XFBML._getDomElements = function(a,b,c) {
return a.getElementsByTagNameNS('http://www.facebook.com/2008/fbml', c);
}
This actually should work as intended, and finds the <fb:like />
elements just fine. If it wasn’t, that is, that the behaviour of the code then went ape, and the button worked intermittently.
At the end of the day, I decided to go with the <iframe>
based version like most of the other websites out there do; it’s quite unfortunate, I have to say. I hope that at some point FBML is replaced with something more along the lines of what Flattr does with simple HTML anchors that get replaced, no extra namespaces to load…
The bright side is that I can actually use FSWS to replace the XFBML code with the proper <iframe>
tags, and thus once it’s released it’ll allow to use at least part of the FBML markup to produce static websites that do not require the Facebook Javascript SDK to be loaded at all… now, if FSF were to answer me on whether I can use text of the autoconf exception as a model for my own licensing I could actually release the code.