What are some of the lesser-known but useful features and techniques that people are using in their Greasemonkey scripts?
(Please, just one feature per answer.)
Similar threads:
What are some of the lesser-known but useful features and techniques that people are using in their Greasemonkey scripts?
(Please, just one feature per answer.)
Similar threads:
Data can be persisted across page loads by storing it as a mozilla preference value via GM_setValue(keyname, value)
.
Here is a simple example that tallys the number of times your script has been executed - by a given browser:
var od = GM_getValue("odometer", 0); od++; GM_setValue("odometer", od); GM_log("odometer=" + od);
GM values are analogous to cookies in that cookie values can only be accessed by the originated domain, GM values can only be accessed by the script that created them.
Anonymous statistics
Assuming you have a basic hosting service that provides access logging, you can easily track basic usage statistics for your script.
var img = document.createElement("img"); img.src = "http://mysite.com/logo.gif"; document.body.appendChild(img);
Now, each time a user executes your script, your hosting service will register a hit on that gif file.
To track more than one script, use a different gif file for each. Or add some kind of differentiating parameter to the URL, (eg: http://mysite.com/logo.gif?zippyver=1.0
).
Greasemonkey scripts often need to search for content on a page. Instead of digging through the DOM, try using XPath to locate nodes of interest. The document.evaluate()
method lets you provide an XPath expression and will return a collection of matching nodes. Here's a nice tutorial to get you started. As an example, here's a script I wrote that causes links in phpBB3 posts to open in a new tab (in the default skin):
// ==UserScript==
// @name New Tab in phpBB3
// @namespace http://robert.walkertribe.com/
// @description Makes links in posts in phpBB3 boards open new tabs.
// ==/UserScript==
var newWin = function(ev) {
var win = window.open(ev.target.href);
if (win) ev.preventDefault();
};
var links = document.evaluate(
"//div[@class='content']//a[not(@onclick) and not(@href='#')]",
document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
for (var i = 0; i < links.snapshotLength; i++) {
var link = links.snapshotItem(i);
link.addEventListener("click", newWin, true);
}
The XPath expression used in the code identifies all a
elements that 1) do not have an onclick
attribute, 2) whose href
attribute is not set to "#"
, and 3) are found inside div
s whose class
attribute is set to "content"
.
Script header values, (@name, @description, @version, etc), can be made retrievable. This is preferable to maintaining the same constant values in multiple places in your script.
See Accessing Greasemonkey metadata from within your script?
Your script can add graphics into a page, even if you don't have any place to host files, via data URIs.
For example, here is a little button graphic:
var button = document.createElement("img"); button.src = "data:image/gif;base64," + "R0lGODlhEAAQAKEDAAAA/wAAAMzMzP///yH5BAEAAAMALAAAAAAQABAAAAIhnI+pywOtwINHTmpvy3rx" + "nnABlAUCKZkYoGItJZzUTCMFACH+H09wdGltaXplZCBieSBVbGVhZCBTbWFydFNhdmVyIQAAOw==" somenode.appendChild(button);
Here is an online image encoder.
And a wikipedia article about the Data URI standard.
A useful XPath technique is to specify your match relative to a node that you have already found. As a contrived example for stackoverflow:
// first we got the username link at the top of the page var hdrdiv = document.evaluate( "//div[@id='headerlinks']/a[1]", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; // now we can retrieve text that follows it, (user's reputation score) // (note that hdrdiv is now the contextNode argument, rather than document) var reptext = document.evaluate( "following-sibling::span", hdrdiv, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; alert("Reputation Score: " + reptext.textContent);
You can match in any direction relative to the contextNode, ancestors, descendants, previous, following. Here is a helpful XPath reference.
GreaseMonkey scripts run when the DOM is ready, so you don't need to add onload events, you just start manipulating the DOM straight away in your GreaseMonkey script.
==UserScript==
...
@require http://ajax.googleapis.com/ajax/framework-of-your/choice.js
==/UserScript==
GM_setValue normally only stores 32-bit integers, strings, and booleans, but you can take advantage of the uneval() method (and a later eval() on retrieval) to store any object.
var foo={people:['Bob','George','Smith','Grognak the Destroyer'],pie:true};
GM_setValue('myVeryOwnFoo',uneval(foo));
var fooReborn=eval(GM_getValue('myVeryOwnFoo','new Object()'));
GM_log('People: '+fooReborn.people+' Pie:'+fooReborn.pie);
I tend to use "new Object()" as my default in this case, but you could also use "({})". Just remember that "{}" evaluates as a string, not an object. As usual, eval() with care.