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 #36. Select Multiple Checkboxes

Toggle series of checkboxes at once in web forms with a Shift-click.

Web-based email is one of the great success stories when it comes to pure web-based applications. But most web mail sites are still more difficult to use than their desktop counterparts. One of the niceties that desktop programs offer is the ability to select multiple items in a list, by clicking the first item and then Shift-clicking another item, to select all the items in between. This hack brings this functionality to web-based applications, allowing you to click a checkbox (for example, to select a message in your web mail inbox) and then Shift-click another checkbox, to select all the checkboxes in between.

The Code

This user script was specifically tested on Hotmail, Yahoo! Mail, and Google Personalized Home Page. By default, it will run on all pages except Gmail, where it is known to cause problems. If you find that it interferes with other sites you use, you should add them to the "Excluded pages" list in the Manage User Scripts dialog.

The basic functionality is fairly straightforward. I do want to draw attention to one specific function: NSResolver. This function is passed as a parameter to the document.evaluate function to execute an XPath query. Firefox's XPath engine uses the NSResolver function to evaluate namespace prefixes in the XPath expression. In XHTML 1.0 and 1.1 pages served with a "Contenttype: application/xhtml+xml" HTTP header, all the elements on the page will be in the XHTML namespace, http://www.w3.org/1999/xhtml. The script checks for this condition by testing whether document.documentElement.namespaceURI is defined. If the namespace is defined, the script constructs an XPath expression with an xhtml: prefix, to find elements in the XHTML namespace. When Firefox's XPath engine evaluates the expression, it calls the NSResolver function to resolve the xhtml: prefix, and then searches for the requested elements in that namespace.

Since we know that XHTML is the only namespace we'll ever use, we cheat a little bit and always return the XHTML namespace from the NSResolver function. But if a page used multiple namespaces (for example, an XHTML document with embedded MathML or SVG data), we could check the prefix parameter in the NSResolver function and return the appropriate namespace for XHTML, MathML, SVG, or any other XML vocabulary we wanted to include in our XPath query.

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

		   // ==UserScript==
		   // @name			 Check Range
		   // @namespace	 http://squarefree.com/userscripts
		   // @description	 Multi-select a range of checkboxes
		   // @include		 *
		   // @exclude		 http*://mail.google.com/*
		   // ==/UserScript==

		   // based on code by Jesse Ruderman
		   // and included here with his gracious permission
		   
		   var elmCurrentCheckbox = null;
		   
		   function NSResolver(prefix) {
			   return 'http://www.w3.org/1999/xhtml';
		   }

		   function selectCheckboxRange(elmStart, elmEnd) {
			  var sQuery, elmLast;
	
			  if (document.documentElement.namespaceURI) {
				  sQuery = "//xhtml:input[@type='checkbox']";
			  } else {
			      sQuery = "//input[@type='checkbox']";
			  }

			  var snapCheckboxes = document.evaluate(sQuery, document, NSResolver,
				  XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);

			  var i;
			  for (i = 0; i < snapCheckboxes.snapshotLength; i++) {
				  var elmCheckbox = snapCheckboxes.snapshotItem(i);
				  if (elmCheckbox == elmEnd) {
				  elmLast = elmStart;
				  break;
				  }
				  if (elmCheckbox == elmStart) {
				  elmLast = elmEnd;
				  break;
				  }
			   }
				   
			   // note: intentionally re-using counter variable i
			   for (; (elmCheckbox = snapCheckboxes.snapshotItem(i)); ++i) {
				if (elmCheckbox != elmStart &&
				elmCheckbox != elmEnd &&
				elmCheckbox.checked != elmStart.checked) {
				// Fire are onclick event instead of modifying the checkbox's
				// value directly, fire an onclick event. Yahoo! Mail and
				// Google Personalize have onclick handlers on their
				// checkboxes. This will also trigger an onchange event,
				// which some sites rely on.
				var event = document.createEvent("MouseEvents");
				event.initEvent("click", true, false);
				elmCheckbox.dispatchEvent(event);
				 }
				 if (elmCheckbox == elmLast) { break; }
				 }
			 }

			 function handleChange(event) {
				var elmTarget = event.target;
				if (isCheckbox(elmTarget) &&
				   (event.button == 0 || event.keyCode == 32)) {
				if (event.shiftKey && elmCurrentCheckbox) {
				selectCheckboxRange(elmCurrentCheckbox, elmTarget);
				}
				elmCurrentCheckbox = elmTarget;
				 }
			 }

			 function isCheckbox(elm) {
			 return (elm.tagName.toUpperCase()=="INPUT" && elm.type=="checkbox"); 
			 }

			 document.documentElement.addEventListener("keyup", handleChange, true);
			 document.documentElement.addEventListener("click", handleChange, true);

Running the Hack

After installing the user script (Tools Install This User Script), log into http://mail.yahoo.com if you have a Yahoo! Mail account. If you have multiple messages in your Yahoo! Mail inbox, select the checkbox next to the first one and then Shift-click the checkbox next to one farther down the list. All the checkboxes in between will be automatically selected, as shown in Figure 4-10.

You can now delete all the messages, mark them as spam, or mark them as read, just as if you had selected each message individually.

Selecting multiple messages in Yahoo! Mail

Figure 4-10. Selecting multiple messages in Yahoo! Mail

The same works in reverse. Clear all the checkboxes by clicking the Clear All link. Select a checkbox halfway down the list, and then Shift-click on the checkbox next to the first message. Again, all the checkboxes in between will be automatically selected.

You can also use the hack entirely with the keyboard. If you press the Tab key enough times, you will see the focus move around the page and eventually on to the first checkbox in the inbox. Press the spacebar to select the message, and then tab several times to set focus on a message halfway down the list. Press Shift-spacebar to select the message, and all the checkboxes in between will be selected.

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