O'Reilly logo

Greasemonkey Hacks by Mark Pilgrim

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Hack #10. Debug a User Script

Learn the subtle art of Greasemonkey debugging.

The actual process of writing user scripts can be frustrating if you don't know how to debug them properly. Since JavaScript is an interpreted language, errors that would otherwise cause a compilation error (such as misspelled variables or function names) can only be caught when they occur at runtime. Furthermore, if something goes wrong, it's not immediately obvious how to figure out what happened, much less how to fix it.

Check Error Messages

If your user script doesn't appear to be running properly, the first place to check is JavaScript Console, which lists all script-related errors, including those specific to user scripts. Select Tools JavaScript Console to open the JavaScript Console window. You will probably see a long list of all the script errors on all the pages you've visited since you opened Firefox. (You'd be surprised how many high-profile sites have scripts that crash regularly.)

In the JavaScript Console window, click Clear to remove the old errors from the list. Now, refresh the page you're using to test your user script. If your user script is crashing or otherwise misbehaving, you will see the exception displayed in JavaScript Console.

Tip

If your user script is crashing, JavaScript Console will display an exception and a line number. Due to the way Greasemonkey injects user scripts into a page, this line number is not actually useful, and you should ignore it. It is not the line number within your user script where the exception occurred.

If you don't see any errors printed in JavaScript Console, you might have a configuration problem. Go to Tools Manage User Scripts and double-check that your script is installed and enabled and that your current test page is listed in the Included Pages list.

Log Errors

OK, so your script is definitely running, but it isn't working properly. What next? You can litter your script with alert calls, but that's annoying. Instead, Greasemonkey provides a logging function, GM_log, that allows you to write messages to JavaScript Console. Such messages should be taken out before release, but they are enormously helpful in debugging. Plus, watching the console pile up with log messages is much more satisfying than clicking OK over and over to dismiss multiple alerts.

GM_log takes one argument, the string to be logged. After logging to JavaScript Console, the user script will continue executing normally.

Save the following user script as testlog.user.js:

	// ==UserScript==
	// @name TestLog
	// @namespace http://example.com/
	// ==/UserScript==

	if (/^http:\/\/www\.oreilly\.com\//.test(location.href)) {
		GM_log("running on O'Reilly site");
	} else {
		GM_log('running elsewhere');
	}
	GM_log('this line is always printed');

If you install this user script and visit http://www.oreilly.com, these two lines will appear in JavaScript Console:

	Greasemonkey: http://example.com//TestLog: running on O'Reilly site
	Greasemonkey: http://example.com//TestLog: this line is always printed

Greasemonkey dumps the namespace and script name, taken from the user script's metadata section, then the message that was passed as an argument to GM_log.

If you visit somewhere other than http://www.oreilly.com, these two lines will appear in JavaScript Console:

	Greasemonkey: http://example.com//TestLog: running elsewhere
	Greasemonkey: http://example.com//TestLog: this line is always printed

Messages logged in Javascript Console are not limited to 255 characters. Plus, lines in JavaScript Console wrap properly, so you can always scroll down to see the rest of your log message. Go nuts with logging!

Tip

In JavaScript Console, you can right-click (Mac users Control-click) on any line and select Copy to copy it to the clipboard.

Find Page Elements

DOM Inspector allows you to explore the parsed Document Object Model (DOM) of any page. You can get details on each HTML element, attribute, and text node. You can see all the CSS rules from each page's stylesheets. You can explore all the scriptable properties of an object. It's extremely powerful.

DOM Inspector is included with the Firefox installation program, but depending on your platform, it might not installed by default. If you don't see a DOM Inspector item in the Tools menu, you will need to reinstall Firefox and choose Custom Install, then select Developer Tools. (Don't worry; this will not affect your existing bookmarks, preferences, extensions, or user scripts.)

A nice addition to DOM Inspector is the Inspect Element extension. It allows you to right-click on any element—a link, a paragraph, even the page itself—and open DOM Inspector with that element selected. From there, you can inspect its properties, or see exactly where it fits within the hierarchy of other elements on the page.

Tip

Download the Inspect Element extension at https://addons.update.mozilla.org/extensions/moreinfo.php?id=434.

One last note: DOM Inspector does not follow you as you browse. If you open DOM Inspector and then navigate somewhere else in the original window, DOM Inspector will get confused. It's best to go where you want to go, inspect what you want to inspect, then close DOM Inspector before doing anything else.

Test JavaScript Code Interactively

JavaScript Shell is a bookmarklet that allows you to evaluate arbitrary JavaScript expressions in the context of the current page. You install it simply by dragging it to your links toolbar. Then you can visit a web page you want to work on, and click the JavaScript Shell bookmarklet in your toolbar. The JavaScript Shell window will open in the background.

JavaScript Shell offers you the same power as DOM Inspector but in a free-form environment. Think of it as a command line for the DOM. You can enter any JavaScript expressions or commands, and you will see the output immediately. You can even make changes to the page, such as creating a new element document.createElement and adding to the page with document.body.appendChild. Your changes are reflected in the original page.

One feature of JavaScript Shell that is worth special mention is the props function. Visit http://www.oreilly.com, open JavaScript Shell, and then type the following two lines:

	var link = document.getElementsByTagName('a')[0]
	props(link)

JavaScript Shell spews out a long list of properties:

	Methods of prototype: blur, focus
	Fields of prototype: id, title, lang, dir, className, accessKey,
	charset, coords, href, hreflang, name, rel, rev, shape, tabIndex
	target, type, protocol, host, hostname, pathname, search, port,
	hash, text, offsetTop, offsetLeft, offsetWidth, offsetHeight,
	offsetParent, innerHTML, scrollTop, scrollLeft, scrollHeight,
	scrollWidth, clientHeight, clientWidth, style
	Methods of prototype of prototype of prototype: insertBefore,
	replaceChild, removeChild, appendChild, hasChildNodes, cloneNode,
	normalize, isSupported, hasAttributes, getAttribute, setAttribute,
	removeAttribute, getAttributeNode, setAttributeNode,
	removeAttributeNode, getElementsByTagName, getAttributeNS,
	setAttributeNS, removeAttributeNS, getAttributeNodeNS,
	setAttributeNodeNS, getElementsByTagNameNS, hasAttribute,
	hasAttributeNS, addEventListener, removeEventListener, dispatchEvent,
	compareDocumentPosition, isSameNode, lookupPrefix, isDefaultNamespace,
	lookupNamespaceURI, isEqualNode, getFeature, setUserData, getUserData
	Fields of prototype of prototype of prototype: tagName, nodeName,

	nodeValue, nodeType, parentNode, childNodes, firstChild, lastChild,
	previousSibling, nextSibling, attributes, ownerDocument, namespaceURI,
	prefix, localName, ELEMENT_NODE, ATTRIBUTE_NODE, TEXT_NODE,
	CDATA_SECTION_NODE, ENTITY_REFERENCE_NODE, ENTITY_NODE,
	PROCESSING_INSTRUCTION_NODE, COMMENT_NODE, DOCUMENT_NODE,
	DOCUMENT_TYPE_NODE, DOCUMENT_FRAGMENT_NODE, NOTATION_NODE,
	baseURI, textContent, DOCUMENT_POSITION_DISCONNECTED,
	DOCUMENT_POSITION_PRECEDING, DOCUMENT_POSITION_FOLLOWING,
	DOCUMENT_POSITION_CONTAINS, DOCUMENT_POSITION_CONTAINED_BY,
	DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC
	Methods of prototype of prototype of prototype of prototype of
	prototype: toString

What's this all about? It's a list of all the properties and methods of that <a> element that are available to you in JavaScript, grouped by levels in the DOM object hierarchy. Methods and properties that are specific to link elements (such as the blur and focus methods, and the href and hreflang properties) are listed first, followed by methods and properties shared by all types of nodes (such as the insertBefore method).

Again, this is the same information that is available in DOM Inspector—but with more typing and experimenting, and less pointing and clicking.

Tip

Like DOM Inspector, JavaScript Shell does not follow you as you browse. If you open JavaScript Shell and then navigate somewhere else in the original window, JavaScript Shell will get confused. It's best to go where you want to go, open JavaScript Shell, fiddle to your heart's content, and then close JavaScript Shell before doing anything else. Be sure to copy your code from the JavaScript Shell window and paste it into your user script once you're satisfied with it.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required