Replace Default Field Values with Labels

Lots of times, it makes sense to pre-populate a form field with some text to guide the user. Usually you see this when space is limited, and the site tries to use the field’s value as a label. However, the field’s value isn’t the label— it’s the value. We see this often when a form has little real estate available to it, or sometimes when the designer doesn’t want to clutter up the interface with extra text.

Examples of fields with pre-populated values

The examples above are from different sites in the wild. Most are for search functions, and one is to subscribe to a mailing list. However, they all share the common trait that they use their values to identify what the form is for. I don’t necessarily like relying on the field’s value to drive the meaning of the field, since at times the value might not be the instructional text that was once there. For example, once a user enters the field and replaces the default text with their own, the instructional text is gone. Perhaps the page gets refreshed and the inputted value remains, but the user has lost focus of what they were doing and now may not understand what that field is for and why it contains their zip code or email address.

Typically speaking, when the user enters into the form field, the default copy vanishes so they can begin typing. However, lots of times when the user clicks out of the field without having typed anything, the default text doesn’t come back. The typical solution for this is to set the field’s value to an empty string (document.form.element.value = "") on focus. Sometimes, the development team had the foresight to capture what was in the field right before it was emptied out, so that when the user leaves it without entering anything, the value can be put back in. Sometimes.

Another issue I’ve run into in the past, from a practical development standpoint is that sometimes a field needs to have a maxlength attribute set. This usually happens with a zip code field. In the US, our zip codes are 5 digits long. In Canada, they’re 6 digits long. When working on sites that only ship to the US and/or Canada, the zip codes want to have that maxlength attribute set to 5 or 6. But what happens in the case when the default text in the field wants to read something like “Zip/Postal Code”? The value gets truncated down to “Zip/P” or “Zip/Po”.

For these reasons, I’ve begun trying to use a different approach for fields that need to have their values act as their labels, and that’s actually placing the label over the field itself. Here’s what the finished product looks like, or you can click through to view it.


And here’s how I put it together using HTML, CSS and jQuery.


Here’s the HTML for it. There’s nothing terribly crazy about it. I’ll step through it below.

First, I tend to wrap my form rows in a DIV or sometimes a P to assist in adding white-space around the rows of elements & labels. I guess I could float the labels & elements and then let the labels clear the floats, but floating form fields can be tricky business and I’ve found this tends to work out cleaner. Next is the span.labelcontainer. This is here so that we can give it positioning, since this technique relies on absolutely positioning the label. You could do without it and put the labelcontainer class onto the DIVs if you only plan on having one label/input pair per line in your form, but if you decide to have multiple fields per line (city, state, zip comes to mind) then you’ll need the spans.

Inside of the span are the actual labels and inputs. Notice the label gets a class nolabel. That’s our hook to find the labels that want to be positioned over the inputs. It’s the combination of .labelcontainer and .nolabel that makes this work. So, apart from an extra (sort-of optional) span and a class attribute, this is pretty bare-bones markup.


The CSS is equally as bare-bones (espeically when I just show you what’s pertinent to making this stuff work).

.labelcontainer { position: relative; }
label.nolabel { position: absolute; top: 3px; left: 6px; font-size: 11px; color: #666; font-style: italic;}

Yup. Two declarations, matching the two unique HTML additions that makes this work. labelcontainer is given relative positioning so that we can position something inside of it. The second declaration does a little bit more. It absolutely positions the label in its containing element (span.labelcontainer) and fancifies the look a little bit. Again, really not very much happening here. If you’re not familiar with absolute vs. relative (vs. static) positioning, Douglas Bowman has a good tutorial over at stopdesign.

Javascript (featuring jQuery!)

And here’s the javascript (using jQuery) for this solution:

$(document).ready(function() {
	$(".labelcontainer input").focus(function() {
		var e = $(this).siblings("label").eq(0);
	}).blur(function() {
		if ($(this).val() == "") {
			var e = $(this).siblings("label").eq(0);

function hideLabels() {
	$(".labelcontainer input").each(function() {
		if ($(this).val() != "") {
			var e = $(this).siblings("label").eq(0);

Since all of the interaction needs to happen on the inputs, we’ll start there. We add a focus handler to the inputs inside of .labelcontainer, since those are the ones we need to worry about. When the input gains focus (it’s clicked on, tabbed into, or activated using a label), we go and find that field’s label. In our sample HTML, that’s the field’s first sibling that’s a label (since the field and label are both wrapped in the same span). Once we’ve got the label in our sights, we’ll hide() it.

Next, thanks to jQuery’s chaining awesomeness, we can also add the blur handler to the same input we’re working on. Blur is the opposite of focus, so when the user tabs out or clicks elsewhere, the blur event is fired. We attach a handler to this event that first checks to see if the field’s value is blank ($(this).val() == ""). If it is, we go and find the label again, but this time we show() it.

After attaching the handlers, we also run a generic function called hideLabels(). If we didn’t have this function and the fields had real actual data in them at page-load (written by the server, or just if the page has been refreshed in some browsers), the labels would obscure the default values. The hideLabels() function simply loops through each of the inputs we targeted above and checks to see if its value is not an empty string. If it’s not (empty), we go and find that field’s label and hide() it.

And there you have it— a fairly straightforward way of keeping your forms accessible, standards-compliant and fancy (with the bonus of getting around that pesky maxlength attribute) all at the same time. Enjoy!