Check it, don't select it

By Nicholas Rougeux, posted on December 17, 2005 in CSS, Web

Usability and accessibility are all the rage right now and that's great because it's slowly making the web a better place. Unfortunately, some of the goodies in our bags of tricks aren't quite up to snuff when it comes to these two areas. One such goody is the good old select element—specifically with the multiple attribute enabled. How about we do away with them and try something different?

The biggest problem with multiple-select boxes is that selecting multiple options is a pain, especially if there are enough to create a scrollbar within the box. The most common place I've seen this scenario is in content management systems. For example, let's say you are writing an article for a site in a CMS and want it to appear in several categories on the front end of a site. (Let's assume that the system is not using tags for this example.) To related it to these categories, a multiple-select box is usually shown with all the categories of the site as options. You would have to hold down Ctrl and select each category. This may include scrolling within the box and if you love using the handy dandy scroll wheel on mice, then you may click in the box again to enable focus and start scrolling down—but wait! You just clicked without holding Ctrl so you just deselected all the previously-selected options. Now you have to go back and remember what you picked and use the scroll bar to scroll down. Sure, you set focus to the element by selecting the elements with Ctrl but not everyone knows that. All this backing and forthing gets to be a real pain.

Why not replace that cumbersome select box with a scrollable checklist? Recently, I was working in Visual Studio and noticed a control called CheckedListBox (who names these things?) and thought I would try to make one for use on the web using some standard elements and a smattering of CSS. Follow along below to see how I did it, see the final basic result or check out some fancy examples.

The first step is to create the structure. I did this by simply using an unordered list, some labels, and checkboxes:

<form action="#" method="get">
    <ul class="checklist">
        <li><label for="o1"><input id="o1" name="o1" type="checkbox" /> Aenean malesuada ante eget tellus</label></li>
        <li><label for="o2"><input id="o2" name="o2" type="checkbox" /> In posuere augue id velit</label></li>
        <li><label for="o3"><input id="o3" name="o3" type="checkbox" /> Nullam a dui ac augue adipiscing sodales</label></li>
        <li><label for="o4"><input id="o4" name="o4" type="checkbox" /> Vivamus consectetuer ante eget urna</label></li>
        <li><label for="o5"><input id="o5" name="o5" type="checkbox" /> Aliquam id felis cursus purus tristique condimentum</label></li>
        <li><label for="o6"><input id="o6" name="o6" type="checkbox" /> Suspendisse posuere lectus vitae velit consequat volutpat</label></li>
        <li><label for="o7"><input id="o7" name="o7" type="checkbox" /> Fusce condimentum nulla et tortor.</label></li>
        <li><label for="o8"><input id="o8" name="o8" type="checkbox" /> Proin consequat faucibus mi</label></li>
        <li><label for="o9"><input id="o9" name="o9" type="checkbox" /> Etiam a lectus quis massa viverra laoreet</label></li>
        <li><label for="o10"><input id="o10" name="o10" type="checkbox" /> Pellentesque gravida lorem vel odio</label></li>
    </ul>
</form>

That class on the unordered list is all we need to make the checklist. What we're going to do is so basic that it can be applied in many areas but I'm just using it for checklists for now. Next, we add a dab of CSS:

.checklist {
    border: 1px solid #ccc;
    list-style: none;
    height: 20em;
    overflow: auto;
    width: 16em;
}

.checklist, .checklist li { margin: 0; padding: 0; }

.checklist label {
    display: block;
    padding-left: 25px;
    text-indent: -25px;
}

.checklist label:hover { background: #777; color: #fff; }

Now we have very basic scrollable checklists without all the hassle of worrying about deselecting previously-selected options. Setting overflow: auto is what creates the scrollable area and with the height and width units set as ems, the entire thing is resizable. We also style the labels to have a block display to make the entire option clickable and add some padding but set a negative text indent so the words can wrap to the next line without appearing underneath the checkboxes. One extra helpful style was added to labels when hovering over them to let users know that they can click on them.

Make IE play nice

We're done, right? Nope. As usual in the world of CSS, Internet Explorer rears it's bothersome head and imposes some unwanted limits. Unfortunately, setting the labels' display property to "block" brings about the infamous white space bug in which IE displays the white space in between the li tags. To solve this, use the Holly hack and add a height declaration to the label properties:

.checklist label {
    display: block;
    height: 1%;
    padding-left: 25px;
    text-indent: -25px;
}

The final thing we need to do for IE is to implement a version of the JavaScript introduced in A List Apart's Suckerfish Dropdowns. This JavaScript allows IE to change the background of the labels just like modern browsers do with the :hover pseudoclass:

function initChecklist() {
    if (document.all && document.getElementById) {
        // Get all unordered lists
	   var lists = document.getElementsByTagName("ul");
        
        for (i = 0; i < lists.length; i++) {
            var theList = lists[i];
            
		  // Only work with those having the class "checklist"
            if (theList.className.indexOf("checklist") > -1) {
                var labels = theList.getElementsByTagName("label");
                
			 // Assign event handlers to labels within
                for (var j = 0; j < labels.length; j++) {
                    var theLabel = labels[j];
                    theLabel.onmouseover = function() { this.className += " hover"; };
                    theLabel.onmouseout = function() { this.className = this.className.replace(" hover", ""); };
                }
            }
        }
    }
}

Load this function when the page loads using Simon Willison's addLoadEvent and you're good to go.

Now you're free to style your checklists however you like and you have much more freedom in doing so than trying to style select boxes. Check out the final result or take a look at some possibilities.

I'm no expert, so if I've made a mistake somewhere or things can be streamlined a bit more, please don't hesitate to offer up suggestions.

Update: Internet Explorer 7 is known to display the scrollable checklists improperly. This is due to the fix applied to make Internet Explorer play nice as mentioned above. Fortunately, the fix is simple. Just remove the height: 1%; declaration and put it on its own line and target IE versions 6 and below:

.checklist label {
    display: block;
    padding-left: 25px;
    text-indent: -25px;
}

* html .checklist label { height: 1%; }

The targeting is done by making use of the popular star hack.

The samples have been updated with the adjustment.

Translations

This article has been translated into the following language:

« Back to blog