Customizing Form Fields with CSS and jQuery

As promised, here is a discussion on how I customized some form elements with a fancy look and feel. It’s really fairly simple and utilizes nothing but standard form controls and some unordered lists.

A Little Background

I was doing some work for a client recently and the creative came over with a bunch of fancy checkboxes and multi-select lists. During a brief kickoff meeting, it was announced that these form controls should toggle on and off just like regular ones, but should look like the ones in the comp. The multi-select lists needed to be just clickable, without the user pressing control or command.

My first reaction was “damn you, mr. fancypants designer!”, but after I thought through it and put my own fancy markup pants on, I came up with what I think is a fairly elegant solution. You can view the demo here.

Markup

The markup for this is very straightforward. I knew I didn’t want to re-invent anything here, so after thinking for a moment about the behavior that was desired and about how standard inputs work, I decided I could do this all with radio buttons and checkboxes. Here’s the HTML for the form (note, this uses a reset stylesheet and jQuery):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<form>
<h2>What's your favorite Javascript Library?</h2>
<ul>
	<li><label for="js_lib-jquery"><input type="radio" name="js_lib" value="jQuery" id="js_lib-jquery" /> jQuery</label></li>
	<li><label for="js_lib-prototype"><input type="radio" name="js_lib" value="Prototype" id="js_lib-prototype" /> Prototype</label></li>
	<li><label for="js_lib-mootools"><input type="radio" name="js_lib" value="MooTools" id="js_lib-mootools" /> MooTools</label></li>
</ul>
 
<h2>Lanugages you use on a daily basis</h2>
<ul class="multiselect">
	<li><label for="lang_html"><input type="checkbox" name="lang" value="HTML" id="lang_html" /> HTML</label></li>
	<li><label for="lang_css"><input type="checkbox" name="lang" value="CSS" id="lang_css" /> CSS</label></li>
	<li><label for="lang_js"><input type="checkbox" name="lang" value="Javascript" id="lang_js" /> Javascript</label></li>
	<li><label for="lang_php"><input type="checkbox" name="lang" value="PHP" id="lang_php" /> PHP</label></li>
	<li><label for="lang_java"><input type="checkbox" name="lang" value="Java" id="lang_java" /> Java</label></li>
	<li><label for="lang_asp"><input type="checkbox" name="lang" value="ASP" id="lang_asp" /> ASP/.NET</label></li>
	<li><label for="lang_ruby"><input type="checkbox" name="lang" value="Ruby" id="lang_ruby" /> Ruby</label></li>
	<li><label for="lang_python"><input type="checkbox" name="lang" value="Python" id="lang_python" /> Python</label></li>
</ul>
 
<h2>Lanugages you wish you didn't have to use on a daily basis</h2>
<ul>
	<li><label for="lang-no_html"><input type="checkbox" name="lang-no" value="HTML" id="lang-no_html" /> HTML</label></li>
	<li><label for="lang-no_css"><input type="checkbox" name="lang-no" value="CSS" id="lang-no_css" /> CSS</label></li>
	<li><label for="lang-no_js"><input type="checkbox" name="lang-no" value="Javascript" id="lang-no_js" /> Javascript</label></li>
	<li><label for="lang-no_php"><input type="checkbox" name="lang-no" value="PHP" id="lang-no_php" /> PHP</label></li>
	<li><label for="lang-no_java"><input type="checkbox" name="lang-no" value="Java" id="lang-no_java" /> Java</label></li>
	<li><label for="lang-no_asp"><input type="checkbox" name="lang-no" value="ASP" id="lang-no_asp" /> ASP/.NET</label></li>
	<li><label for="lang-no_ruby"><input type="checkbox" name="lang-no" value="Ruby" id="lang-no_ruby" /> Ruby</label></li>
	<li><label for="lang-no_python"><input type="checkbox" name="lang-no" value="Python" id="lang-no_python" /> Python</label></li>
</ul>
</form>

As you can see, we’ve got a somewhat invalid form element with some nicely organized headings and ULs. The important part are the radio buttons/checkboxes and the all-important labels. You may notice I’m using both implicit (labels wrapping the elements) and explicit (standalone labels using the “for” attribute) labels for the form elements. Using the implicit method makes life a lot easier when it comes to the jQuery, as we can simply step up one element from the input to find the label. However, IE6 chokes when the “for” attribute isn’t linked up to an input, thereby making the implicit method useless in that browser. Therefore, we have to add that back in to help those people. And boy, do they need help! Either way, all by itself, this markup would work (save for the missing attributes in the form, but whatever– this is a demo). Now for the fun stuff!

Styling The Elements

The CSS for this is also pretty simple. I’ll drop it here, then we can roll through the super-important parts.

1
2
3
4
5
li { position: relative; margin: 5px 0;}
li input { position: absolute; left: -9999px; }
li label { padding-left: 20px; background: #FFF url('images/checkboxes.gif') no-repeat scroll 0 -21px; cursor: pointer; }
li label.checked { background-position: 0 0; }
ul.multiselect { height: 85px; overflow-y: scroll; border: 1px solid #000; border-right: none; }
  • First, the LIs have position: relative This gives elements inside of them a new positioning reference point.
  • Next, the inputs within the LIs are given an absolute position and they’re offset to the left by 9999px. This will effectively push them way off the screen of on any “normal” monitor in 2009.
  • With the actual form element out of the way, we can replace it with a fancy image, in this case a background image called “checkboxes.gif”. This image is actually a CSS sprite and covers both the un-checked and checked-states of all of the inputs. I also gave the label a pointer cursor so that the user knows they can click on it.

At this point, we’ve got a bunch of labels with background images that look like empty checkboxes. As a rule of thumb, when you’re mucking with system-driven elements (form fields, inputs, scrollbars, etc.), it’s extremely important to make sure the user still recognizes them as functional parts of the page, hence the cursor: pointer; declaration on the labels.

  • The next line of CSS adds a “checked” class to the labels which simply re-position the sprite to the “checked” position.
  • Finally, we dress up the .multiselect UL with a border, a height and some overflow magic to throw a scrollbar if the contents overflow the bounds of the box. This is the element that pretends to be a <select multiple="true">...</select> box, so again, we need to make the user think that’s what it actually is.

Now that we’ve got our form all styled up and that .checked class kicking around, we’re ready to wire it up with the jQuery business.

Rolling in the jQuery

The thing I love about jQuery is that its selector syntax is the same as CSS, which I already use all day long to style elements in the DOM. Why not use that to mess with them too? Here’s the listing, then we’ll roll through it again.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
$(document).ready(function() {
 
	$("input[type!='submit']").click(function() {
		var label = $(this).parent("label");
		var labels = $(this).parents("ul").find("label");
 
		if ($(this).attr("type") == "radio") {
			$(labels).each(function(i) {
				$(this).removeClass("checked");
			});
		}
 
		if ($(this).attr("checked") == true) {
			$(label).addClass("checked");
		} else {
			$(label).removeClass("checked");
		}
	});
 
	$("input[type!='submit']").each(function(i) {
		var label = $(this).parent("label");
		if ($(this).attr("checked") == true) {
			$(label).addClass("checked");
		} else {
			$(label).removeClass("checked");
		}
	});
 
});

Click Events

Two major parts to this madness. The first is the click handler that we’ll attach to the form elements. jQuery passes click events on the labels into click events on the form elements themselves and keyboard activation on a form element also triggers its click event, so I’ll attach the events to the element’s click and not the label’s.

  • First, I set a couple of variables so that I don’t have to keep traversing the DOM for the things I need. label is set to the clicked element’s associated label, and labels is set to all of the labels in the current element’s parent UL.
  • Next, we check to see if the clicked element is a radio button. If it is, that means that no other radio buttons of the same name can be checked at the same time, so we step into an each loop (a jQuery for loop) and remove the checked class from all of the labels.
  • Then, we check the element the user clicked on itself to see if it now has an actual “checked” attribute, and if it does, we apply the checked class to it, otherwise we remove the class.

Setting Default States

The next function sets default states in the event the form is pre-populated by some database magic (or if the user made some selections and hit refresh).

  • Step into an each loop on all of the inputs that aren’t submit buttons.
  • Stash the label of that input into a variable called label
  • Again, check to see if this element’s “checked” attribute is set to true and if so, apply the checked class to it, otherwise remove it

And there you have it, custom checkboxes, radio buttons and multi-select lists with jQuery and CSS. It wouldn’t be too hard to extend the multi-select list into a traditional select list that allows the user to click on the default element and have the whole list expand, and then collapse once a selection is made.

5 thoughts on “Customizing Form Fields with CSS and jQuery

  1. I like it Rob! Of course, the only problem is your form renders completely useless if JavaScript is disabled. You could correct this by having your list dynamically created using the DOM, and throwing in a straight-forward, non-mr-fancy-pants checkbox/radio button list in a noscript tag. Well, that’s what I can think of right off the top of my head. You might be able to think of a better way.

  2. @Mike – Thanks for the note! Technically, it’s not USELESS, as the click events still fire. Without JS, you just don’t SEE them fire (test: disable JS, click around the form; disable CSS, see your selections). Another option is to load this bit of CSS inside of an if (document.getElementById()) block. That way, the form isn’t destroyed in the face of no JS.

  3. I see you share interesting things here, you can earn some
    additional cash, your website has big potential, for the monetizing method, just search in google – K2 advices how to monetize a
    website

Leave a Reply

Your email address will not be published. Required fields are marked *