Monday, November 12, 2007

Blocking Facebook News Feed Ads with Greasemonkey

I hate ads, so I block them. If I want something, I'll search it out. I don't need marketers wasting my time trying to convince me that I need something I don't. Most of the time I have ads blocked by aliasing most known ad servers to 127.0.01 in /etc/hosts/. This only works when the ads are coming through a server that is listed in the file (say, ad.doubleclick.net) and not when the ads embedded in the page are served from the same server from which the original page was requested.

This is exactly the problem with the ads from Facebook, which just deployed a new ads system that has ads targeted on what you've listed in your profile. For example, if you listed Jane Austen novels as some of your favorite books, you might see text ads in the sidebar or in your news feed advertising "Jane Austen: The Ultimate Sappy Romance Collection" ... or something. So how to block these ads?

Greasemonkey is a Firefox (and IE, although I wouldn't know much about that :-) ) extension to inject arbitrary Javascript into web pages, meaning that you can manipulate a page, or parts of a page, automatically as you see fit. Its behavior is defined by scripts, many of which can be found at userscripts.org. If you're familiar with web design, you can write your own scripts quite easily. Mozdev has a good quickstart guide, and Mark Pilgrim has an online guide and a book if you want to research this further.

There are a few ways to do block Facebook's new ads. The first is using document.getElementsByTagName('div') to fetch all the divs in the page and then loop through them, testing them for known ad-serving attributes (for example, a className like 'social_ad_advert' (sidebar) or 'feed_item clearfix social_ad' (news feed) or an id of 'ssponsor', 'sponsor', or 'announce' (all banner ads)) [1] and then either hiding them with element.style.display = 'none' or removing the element entirely using element.parentNode.removeChild(element). A second is using XPath, which makes for much more compact code. [2] userscripts.org is awash in (mostly mediocre [3]) scripts to block Facebook ads, but the most comprehensive one I found was "Remove All Facebook Ads" which I'm using in my browser right now. The only suggestion I have for this script would be to use the window.addEventListener to trigger execution of the code rather than embedding it in an anonymous function, as explained in the quickstart guide (under "Tips").

Greasemonkey scripts are executed after the DOM is loaded, which means that all the ads will be fetched from their sources and displayed on the page before they can be stomped out, unfortunately. Therefore, to make the page load faster by not fetching and displaying ads in the first place, it would be wise to use an /etc/hosts blocking scheme instead of a Greasemonkey solution where possible. Greasemonkey should be used only as a last resort in combination with /etc/hosts where /etc/hosts can't block ads that aren't fetched from a server different than the one used for the requested page, as in the case of Facebook news feed ads.

[1] It would probably be faster to not test for the id matches and just using getElementById for those few cases outside of the loop instead (within try-catch blocks if the elements aren't guaranteed to be on to page and could potentially throw an error).
[2] I'm not sure about the efficiency implications of using one vs the other. Anyone care to comment?
[3] For instance, "Hide Facebook Ads" tries to do some browser detection... I'm not sure how large the market for IE4 Greasemonkey users is...

UPDATE:

Now using New Facebook Layout Ad Killer for the new Facebook layout

Argh, it seems Facebook has added another lame ad sidebar. Just extend the NFLAK script by looking for a document ID called 'fadbar' and hide it if it's there.