There was some excitement a while back about the potential of user style sheets and CSS signatures. By applying personal CSS rules, users can browse the web the way they, not the page designers, want. If underlined links offends your aesthetic sensibilities, you can have them rendered plain. If you agree with Jakob Nielsen, you can underline links which uncaring designers have left tragically unadorned.
By applying site-specific personal CSS rules, users can quite literally redesign the sites they browse. The body element on Eric Meyer’s pages, for example, always has an id of www-meyerweb-com. Since, personally, I’m not a big fan of the blue entry headings, I can put a rule like this in my user CSS file:
#www-meyerweb-com h3 a { color: black; }
Simply done. Throw in a browser supporting XBL and the possbilities are limitless.
What really has me excited, though, is the idea of site-specific user JavaScript.
Like CSS signatures, it’s about users modifying webpages for their own benefit. But unlike CSS, JavaScript can enable new functionality — we’re not just changing a page’s appearance, we’re changing its function. (Much of what I’m going to discuss can be done with CSS and XBL, but it’s essentially the same thing — just a different method of calling the scripts.)
Probably the best example of site-specific user JavaScript is Adrian Holovaty’s All Music Guide Corrector for Firefox. When allmusic.com brought in a new design far less functional than the previous version, Holovaty’s extension provided a host of usability improvements.
For example:
It changes the functionality of the “Read more…” links on band and album detail pages. On the old AMG, band and album pages contained full reviews. Now, they feature only the first few sentences, with a link to “Read more…” on a separate page. The extension changes the functionality of that “Read more…” link so that, instead of taking you to a new page, clicking the link will dynamically load the full band/album review and insert it inline.
Building extensions is quite easy (if I can do it, anyone can), but it’s still a significant barrier to entry. GreaseMonkey largely removes that barrier, by providing a simple interface for applying site-specific scripts. Already we’ve got fixes for problems at MSDN and BlogLines, and scripts to remove interstitial ads at Yahoo! Groups and various gaming websites.
It’s simple. Think of a site you use, and something about it that annoys you. Something that could make it better. With JavaScript, you can probably do it.
It might be ugly, but you can make it work.
By way of example, I hacked together an ugly little Kuro5hin.org comment killfile script. The site is a community-driven website with little admin intervention, so it tends to attract trolls and crapflooders. They’re often more entertaining (and, regretfully, more intelligent) than the serious posters, but the abusive ones are another story. The idea of a killfile has been dismissed whenever it’s been suggested — for various reasons, most of which I actually agree with — but that doesn’t mean one can’t be useful.
Kuro5hin is a good example for another reason: it’s quite old, and uses thousands of horrible nested tables in its markup. Since JavaScript/DOM functions for manipulating pages rely on semantic markup, doing anything useful at Kuro5hin — where the markup contains many style attributes and not a single id — is a bit of a chore.
First, we set a list of usernames to block.
list = new Array("rusty", "rmg", "kitten");
There are many different ways to approach the filtering, but I’m going to select every comment then match it against the list. It’s probably better to only select the matching comments, but this is what I did first.
It’s possible to use getElmementsByTagName or similar, but I think it’s faster (and definitely easier) to use an XPath expression. This one selects every <a href="/user/uid:x">username</a> element. That’s almost incidental, actually; the real meat of it is matching font tags containing the text “by “, which occurs only in comments. It’s a terrible, terrible hack, but that’s the point: there are almost always several ways, some hideous, to select the content you want.
var xpath = "//a[parent::font[text()[1]='by ']][1]";
var a = document.evaluate(xpath, document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
var elm, i;
Now go through them all, and check the username on each comment against the list to block.
for(elm = null, i = 0; (elm = a.snapshotItem(i)); i++) {
for (l=0; l<list.length; l++) {
if (elm.firstChild.nodeValue == list[l]) {
If you thought the XPath search was a hack, just wait for this. It selects the great grandfather table, the base of the comment, of the a element we found. It’d be more elegant to use another XPath expression — maybe with ancestor-or-self:: — but again, this method works.
comment = elm.parentNode.parentNode.parentNode.
parentNode.parentNode.parentNode.parentNode.
parentNode.parentNode.parentNode.parentNode.
parentNode.parentNode;
Now remove the comment.
comment.parentNode.removeChild(comment);
And lo, we have a simple killfile. You might see the comments if you browse the page while it’s still loading, but after that they’ll be all gone. Not just hidden, but removed from the document tree. Since even the bad trolls can make good comments on occasion, I wrote an expanded version that just makes the comments unobtrusive, but with a link on each one to display it.
It’s trivial to extend into different types of filtration is easy enough. Do you want to hide every comment below a certain rating threshold? Do you want to remove every comment containing the words “Nazi” or “Hitler” in a preemptive attempt to foil Godwin’s Law? It’s not something I’m going to bother with (I don’t even plan to use this script), but that’s just the thing — user scripts are empowering, because people can do whatever they want. It doesn’t matter that other users don’t want the same functionality — they never even have to know it exists.
I’m not sure why it took me so long to find this entry, but it’s now added to the User Script directory.
http://69.90.152.144/collab/GreaseMonkeyUserScripts
Also, added a bug for Greasemonkey to request a simple UI to allow user scripts to be edited without digging in the profile directory.
http://www.mozdev.org/bugs/show_bug.cgi?id=9281
Good god, I wish I could block diaries as well - I don’t have the l33t skillz needed to do this.