Part 3 - Alter specific element

OK, so now you can change a single style aspect by changing the DOM object, or alter lots of style aspects by putting in your own CSS; and you can make that site-specific via the include/exclude rules that are built into Greasemonkey. What about when you want to find a particular type of element that occurs multiple times on a page, and alter it?

For example, maybe you're using a forum where sometimes people have non-work-friendly user icons, so you want to just replace all the images with a nice safe image that you have somewhere on your own webspace. So, first of all, let's find all the images on the page:

var allImgs,thisImg;
allImgs = document.evaluate('//img[@src]',
document,
null,
XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);

The good bit here is the document.evaluate method. Getting the hang of this will give you an enormous amount of power. The first parameter ('img[@src]') is an XPath query (of which more in a moment); the second is the element that you want to search.

In this case that's the whole document, but you could restrict it further, and the query will only look through elements that are children of that element.

The third parameter is a namespace resolver function, which is only relevant to application/xhtml+xml type pages.

The fourth parameter is how you want your results returned. The type here gives them back in random order, which is usually fine; if for some reason you want them back as they occur in the page, use XPathResult.ORDERED_ NODE_SNAPSHOT_TYPE instead.

The final parameter allows you to merge query results – pass in the result of a previous document.evaluate call and it'll merge the two together. Feel free to experiment with this!

The XPath query (the first parameter) is the powerhouse of this function. XPath is a powerful XML query language which is built in to Firefox and which you can therefore use from within Greasemonkey.

If you want to find a particular set of elements, you could crawl through the DOM tree, retrieving sets of nodes and searching through them for whatever it is you're after.

However, this is pretty slow and a bit ugly in terms of code. XPath gives you a quicker and cleaner way of finding pretty much anything you care to specify in a web page. I'll use it in a couple of scripts here, which will hopefully give you an idea of how it works – check out the specification online or one of the available tutorials if you want to know more about this.

It's really very flexible: if you can think of a set of results you want to get out of an HTML document, you can probably construct an XPath query that will return them for you.

Back to our script: now you've got your images, you want to do something with them. This next piece of code goes in the same script, after the bit above:

for (var i=0;i<allImgs.snapshotLength;i++) {
var thisImg = allImgs.snapshotItem(i);
var src = thisImg.src;
var srcMatch = src.match('^http://www.example. com/forums/userpic/');
if (srcMatch != null) {
thisImg.src = 'http://www.example.com/ juliet/safepic.gif';
}
}

snapshotLength and snapshotItem are provided methods that work on the result of a document.evaluate call and give you, respectively, the total number of items returned, and a particular item. So you can stick both of those into a for loop, as here, and iterate over every result (here, every image on the page) returned by your XPath query. A quick note: in regular JavaScript, you could iterate over this collection like so:

for (var thisImg in allImgs) {
// do stuff
}

Because of the way that Greasemonkey implements script security, this won't work in a Greasemonkey script. You need to do it the long way around.

thisImg.src gives you the value of the src attribute of the image. So if your image tag were <img src="foo.jpg">, thisImg.src would return foo.jpg. (You can access the width or height attributes, or any other img attribute, with a similar syntax. Check out DOM object references online for more information.)

The final section tries to match the src value with the value expected for userpics on this forum (to find this for your own forum, you would need to take a look at the source code or a page of it), and if the result is non-null (indicating that there is a match), it replaces the image src value with your safe image. (You could write this in one fewer line by putting the src.match call in there directly, but this is a little easier to read.) And we're done!