Adding checkboxes to lists

Matt Gemmell had an interesting idea this evening:

Thought of a useful Safari extension: change any UL/OL elements on the page into checkboxes, for when you’re working through a tutorial.

I decided to see if it would be possible, and in doing so discovered how rusty I’ve become at JavaScript. This is simple enough that it can be done with a bookmarklet rather than a browser extension, and that’s what I’ve done here.

Drag this bookmark to your bookmarks bar:

Add checkboxes to lists

Simply click it once, and all the UL/OL elements will have their bullets replaced by checkboxes.

If you want a quick demo, click the link above and see what happens to the following lists:

An unordered list:

An ordered list:

  1. Do the first step
  2. Then do the second step
  3. Finally, do the third step

I’ve given this light testing on the latest versions of Safari and Chrome on OS X, but that’s it. (Using this will also allow you to discover all the interesting ways in which lists are used for page layout, when they break.) This was just a bit of fun; I don’t expect it to be a perfect solution.

How it works

I’ve done something very similar before. We use JavaScript to get all the UL and OL elements, then apply the appropriate collection of styles to create the effect we want.

First we get a collection of all the UL and OL elements on the page.

var ul_lists = document.getElementsByTagName("ul");
var ol_lists = document.getElementsByTagName("ol");

Next we go through all these UL/OL elements, and add a few CSS attributes. Here I’m hiding the visible bullet, and adding some padding so that the checkbox is indented nicely:

function update_list(list) {
    list.style.listStyle = "none";
    list.style.webkitPaddingStart = "10px";
    list.style.MozPaddingStart = "10px";
}

for (var idx = 0; idx < ul_lists.length; idx++) {
    update_list(ul_lists[idx]);
}

for (var idx = 0; idx < ol_lists.length; idx++) {
    update_list(ol_lists[idx]);
}

It would probably be better to join ul_lists and ol_lists into a single iterable, but it wasn’t immediately obvious how to do that (getElementsByTagName() returns an HTMLCollection instead of an Array), so I just went with what was easy.

Next we have to add the checkbox to each LI element. Again, we use getElementsByTagName() to get all the LI elements, and add some HTML within the <li>...</li> tags. I’m throwing in a non-breaking space after the checkbox to add a little spacing between it and the associated text:

var items = document.getElementsByTagName("li");
for (var idx = 0; idx < items.length; idx++) {
    items[idx].insertAdjacentHTML('afterbegin', '<input type="checkbox" style="font-size: 1.5em;">&nbsp;');
}

Then I used John Gruber’s JavaScript bookmarklet builder to wrap it into a bookmarklet, and installed it in my browser.

[However, I’m not quite using the raw bookmarklet in the big link above. Gruber’s Perl script doesn’t escape the angle brackets in the insertAdjacentHTML() line, rightly so, but it confused my Markdown parser and the link overflowed into the entire page. I’ve had to escape those as well.]

Somebody who knows what they’re doing could probably whip that up in less than two minutes. It took me more like twenty, just because I don’t write very much JavaScript. But it feels good to spend time doing something a bit different to my usual work, and to prove to myself that I haven’t forgotten everything I know about JavaScript.