Flexible and Semantic Forms with a Little jQuery

While they may not be that pretty, forms are one of the most important parts of a web site. I have already written an article on CSS Forms, that was over a year ago, and I have developed much better techniques.

There are other great resources for information about form design, and I will take this information into account when I am showing the flexibility of my method of marking up and styling forms.

The Markup

After previously using a definition list to mark up my forms, I have switched to using an ordered list now. I switched for two main reasons:

  • The list item of the ordered list provides a containing clearing element that the definition list does not provide.
  • Semantically, it just makes more sense. A form is really just a list of form fields to fill out. Forms are typically meant to be filled out in order, hence the ordered list instead of un-ordered list (I think this could be debated, but in the long run, either one is more semantic than anything else).

I have decided to create just a simple un-styled form to use as an example. Here is what the markup of the page is:

<ol class="forms">
 <
li><label for="name">Name</label><input type="text"name="name" id="name" /></li>
 <
li><label for="email">Email</label><input type="text" name="email" id="email" /></li>
 <
li><label for="city">City</label><input type="text" name="city" id="city" /></li>
 <
li><label for="state">State</label><input type="text" name="state" id="state" /></li>
 <
li><label for="zip">Zip</label><input type="text" name="zip" id="zip" /></li>
 <
li class="grouping"><label>Sign Up for the Following</label>
  <
ul>
   <
li><input type="checkbox" name="info1" id="info1" /><label for="info1">Information 1</label></li>
   <
li><input type="checkbox" name="info2" id="info2" /><label for="info2">Information 2</label></li>
   <
li><input type="checkbox" name="info3" id="info3" /><label for="info3">Information 3</label></li>
  </
ul>
 </
li>
 <
li><label for="message">Message</label><textarea name="message" id="message"></textarea></li>
 <
li class="buttons"><button type="submit" name="submit" id="submit">Submit</button></li>
</
ol

Even without any style applied, the form is still completely usable.

Styling the Forms

There are really 3 main styles of laying out forms:

  • Labels above the form field
  • Labels to the side of the form field, jagged right
  • Labels to the side of the form field, jagged left

Again, like I said, I am not going to go through the pros and cons of each method, I am going to show how easy it is to switch between the methods using my markup.

Labels Above the Form Field

This is by far the easiest method. Here is the CSS used to style my example form:

ol.forms { floatleft; list-stylenonewidth100%; }
ol
.forms li { 
 clear
both
 
floatleft
 
margin0 0 10px
 
width100%; 
}
ol
.forms label { cursorpointerdisplayblockfont-weightbold}
ol
.forms inputol.forms textarea { 
 font
inherit;
 
padding2px;
 
width300px;
}
ol
.forms textarea { height250px}
ol
.forms li.grouping { margin-bottom0}
ol
.forms li.grouping ul { list-stylenone}
ol
.forms li.grouping ul label { 
 display
inline;
 
font-weightnormal;
 
margin0 0 0 10px;
}
ol
.forms li.grouping ul input { widthauto

Really nothing too complicated. In my opinion, I don’t like stacking the label over the form field; I think it just makes the form too long.

Labels to the Side of the Form Field, Jagged Right

I like this layout of forms the best. I think it presents the form in a clear way so that you can just scan right through the form fields. With just a few changes to the CSS, you can float the labels to the left of the form fields. The changes are marked in bold:

ol.forms { floatleft; list-stylenonewidth100%; }
ol
.forms li { 
 clear
both;
 
floatleft;
 
margin0 0 10px;
 
width100%;
}
ol
.forms label { 
 cursor
pointer;
 
displayblock;
 <
strong>floatleft;</strong>
 
font-weightbold;
 <
strong>margin0 10px 0 0;
 
width90px;</strong>
}
ol
.forms inputol.forms textarea { 
 font
inherit;
 
padding2px;
 
width300px;
}
ol
.forms textarea { height250px}
<strong>ol.forms li.grouping label { margin0widthauto}</strong>
ol.forms li.grouping { margin-bottom0}
ol
.forms li.grouping ul { list-stylenone; <strong>margin-left100px;</strong}
ol
.forms li.grouping ul label { 
 display
inline;
 <
strong>floatnone;</strong>
 
font-weightnormal;
 
margin0 0 0 10px;
 <
strong>widthauto;</strong>
}
ol
.forms li.grouping ul input { widthauto}
<strong>ol.forms li.buttons { floatnonemargin-left100pxwidthauto}</strong

Take a look at the example with the fields on the left. Depending upon your label length, you may need to increase the width of the labels so that it does not span more than one line, but that is an easy enough change.

Labels to the Side of the Form Field, Jagged Left

Now with two changes to the CSS, you can easily switch to having the labels jagged left, which puts them closer to the form input. The changes needed are highlighted in bold:

ol.forms label { 
 cursor
pointer;
 
displayblock;
 
floatleft;
 
font-weightbold;
 
margin0 10px 0 0;
 <
strong>text-alignright;</strong>
 
width90px;
}
ol
.forms li.grouping label { margin0; <strong>text-alignleft;</strongwidthauto

Here is the example of the form with the labels jagged left.

Taking it a Step Further with More CSS and a Little jQuery

Since I like the form layout with labels to the side and jagged right, I am going to use this one to customize the display a little, and then add in some jQuery functionality.

Improving the Display

When looking at the form, it may be confusing to have all form fields be the same width. It is helpful to the user to modify the width to suit the data that will be entered.

For example, there is no reason that the zip code, city, and state fields should be so long, so let’s add a class to each one to shorten it to the length of the data that will probably be entered. Here is the modified markup:

<li>
 <
label for="city">City</label>
 <
input type="text" name="city" id="city" class="medium" />
</
li>
<
li>
 <
label for="state">State</label>
 <
input type="text" name="state" id="state" class="small" />
</
li>
<
li>
 <
label for="zip">Zip</label>
 <
input type="text" name="zip" id="zip" class="extraSmall" />
</
li

Then we just have to add those 3 classes to the CSS:

ol.forms .extraSmall { width50px}
ol
.forms .small { width100px}
ol
.forms .medium { width150px

Here is what the form looks like with that little bit of extra style. I also added in some help text, a notation for required fields, and the associated CSS; so take a look at the code to see that.

Adding a Little jQuery

Obviously you have read my article about AJAX forms with jQuery, which discusses how to add AJAX functionality to a form in a degradable way. So the following code assumes that you are going to be doing server side validation of the form as well.

Since there are required fields in this form, I add a class of required to the form and to each parent list item of required input fields.

First, we want our JavaScript to execute when the form with a class of required is submitted:

$(document).ready(function() {
 
$('form.required').submit(function() {
  
&hellip;
 
});
}); 

If there are any error messages still displaying from before, we want to hide them. Also, let’s disable the submit button so just in case, the form cannot be submitted twice. I am also creating a variable that I will be using later to determine if there is an error or not:

$(document).ready(function() {
 
$('form.required').submit(function() {
  
<strong>$('form.required span.error').remove();
  $(
'form.required li.buttons button').attr('disabled','disabled');
  var 
hasError false;</strong>
 
});
}); 

Now, we want to check each form field that has a parent list item with a class of required:

$(document).ready(function() {
 
$('form.required').submit(function() {
  
$('form.required span.error').remove();
  $(
'form.required li.buttons button').attr('disabled','disabled');
  var 
hasError false;

  <
strong>jQuery.each($('form.required ol.forms li.required'),function() {
   
&hellip;
  
});</strong>
 
});
}); 

There are a lot of other jQuery plugins that validate forms, but most of the time, it will just alert the user that the field is required. Instead of doing that, I am going to grab the text from the label element and display that in the error message:

$(document).ready(function() {
 
$('form.required').submit(function() {
  
$('form.required span.error').remove();
  $(
'form.required li.buttons button').attr('disabled','disabled');
  var 
hasError false;

  
jQuery.each($('form.required ol.forms li.required'),function() {
   
<strong>var labelText = $(this).children('label').text();
   
labelText labelText.replace(' *','');</strong>
  
});
 
});
}); 

Now, we have two different structures of form fields for each list item: a single form field and a grouping of checkboxes or radio buttons. We have to do things a little bit different for each one:

$(document).ready(function() {
 
$('form.required').submit(function() {
  
$('form.required span.error').remove();
  $(
'form.required li.buttons button').attr('disabled','disabled');
  var 
hasError false;

  
jQuery.each($('form.required ol.forms li.required'),function() {
   
var labelText = $(this).children('label').text();
   
labelText labelText.replace(' *','');

   <
strong>if($(this).hasClass('grouping')) {
    
&hellip
   
else {
    
&hellip;
   
}</strong>
  
});
 
});
}); 

First, let’s focus on the case when there is a grouping. We want to loop through each input and see if it is checked. If it is, we just increment a counter. Then, if the counter equals zero, meaning we have no fields checked, we display an error message:

$(document).ready(function() {
 
$('form.required').submit(function() {
  
$('form.required span.error').remove();
  $(
'form.required li.buttons button').attr('disabled','disabled');
  var 
hasError false;

  
jQuery.each($('form.required ol.forms li.required'),function() {
   
var labelText = $(this).children('label').text();
   
labelText labelText.replace(' *','');

   if($(
this).hasClass('grouping')) {
    
<strong>var numSelected 0;
    
jQuery.each($(this).find('input'),function() {
     
if($(this).attr('checked') == true{
      numSelected
++;
     
}
    }
);
    
    if(
numSelected == 0{
     
$(this).append('<span class="error">You must select from '+labelText+'.</span>');
     
hasError true;
    
}</strong>
   
else {
    
&hellip;
   
}
  }
);
 
});
}); 

That seems easy enough, and it’s even easier for the single input fields. We just check to see if the value is empty, and if it is, display the error message:

$(document).ready(function() {
 
$('form.required').submit(function() {
  
$('form.required span.error').remove();
  $(
'form.required li.buttons button').attr('disabled','disabled');
  var 
hasError false;

  
jQuery.each($('form.required ol.forms li.required'),function() {
   
var labelText = $(this).children('label').text();
   
labelText labelText.replace(' *','');

   if($(
this).hasClass('grouping')) {
    
var numSelected 0;
    
jQuery.each($(this).find('input'),function() {
     
if($(this).attr('checked') == true{
      numSelected
++;
     
}
    }
);
    
    if(
numSelected == 0{
     
$(this).append('<span class="error">You must select from '+labelText+'.</span>');
     
hasError true;
    
}
   } 
else {
    
<strong>if(jQuery.trim($(this).children('input, textarea').val()) == ''{
     
$(this).append('<span class="error">Please enter your '+labelText+'.</span>');
     
hasError true;
    
}</strong>
   
}
  }
);
 
});
}); 

To finish up, we just check to see if the hasError variable is true. Since this is just a demo form, I just add an alert if the form would submit successfully:

$(document).ready(function() {
 
$('form.required').submit(function() {
  
$('form.required span.error').remove();
  $(
'form.required li.buttons button').attr('disabled','disabled');
  var 
hasError false;

  
jQuery.each($('form.required ol.forms li.required'),function() {
   
var labelText = $(this).children('label').text();
   
labelText labelText.replace(' *','');

   if($(
this).hasClass('grouping')) {
    
var numSelected 0;
    
jQuery.each($(this).find('input'),function() {
     
if($(this).attr('checked') == true{
      numSelected
++;
     
}
    }
);
    
    if(
numSelected == 0{
     
$(this).append('<span class="error">You must select from '+labelText+'.</span>');
     
hasError true;
    
}
   } 
else {
    
if(jQuery.trim($(this).children('input, textarea').val()) == ''{
     
$(this).append('<span class="error">Please enter your '+labelText+'.</span>');
     
hasError true;
    
}
   }
  }
);

  <
strong>if(hasError{
   
$('form.required li.buttons button').removeAttr('disabled');
   return 
false;
  
else {
   alert
('The form would submit now');
   $(
'form.required li.buttons button').removeAttr('disabled'); //Remove this line if using for real
   
return false//Change this to true when using it for real
  
}</strong>
 
});
}); 

Go ahead and take a look at the demo form to see it in action.

Conclusion

With a well structured form, you can easily customize it to your liking with just a little bit of CSS. Then, by using some custom jQuery code, you can easily customize how you would like to have the form validation appear.

With a little more work, you could easily add in validation for things like form elements expecting an email address, zip codes, phone numbers, etc. I will have to save that for another post since this one is quite long.

What do you all think? How do you structure your forms? Which layout of forms do you prefer and why?

9 Comments

fabian

08.03.2008

Labels should be aligned right for easy reading.

Tobi

12.09.2008

nice one, but ou should have included a check for a valid email adress..

Willabee

03.06.2009

Demo does not validate strict because you need to wrap your JavaScript in a CDATA section.

It would be nice to see how you style radio inputs. Do you treat them in the same way as your demo’d checkboxes?

I do progressive enhancement but you’ve sold me on jQuery.

Good to grab the label names as part of your error content and the CSS width assignment to inputs. What’s your thoughts on adding maxlength (That agrees with database data lengths) and tabindex (To correspond with your ordered list).

A great project for you (I think) would be to have a server process that can generate an object in JSON format that picks up all the validation rules for a table from your database.
The server-side validation would convert JSON into an object and do its validation.
At the client, jQuery could grab the same JSON string as an object for client-side validation.
Demonstrate the form with no progressions, just SEO friendly, accessible and does correct server-side sanitation and validation.
Then add the CSS to make it pretty.
Then add the jQuery to enhance the form, and validate it.
A great example of PE (progressive enhancement) and DRY (don’t repeat yourself) implimentation.

I find there are lots of good client-side behaviours that never show you what takes place on the server and how server processes have to be changed to call another process (web service), that can be shared with XHR requests.

Sorry for the long comment but I like your work as you are doing all the right things in my mind and your better placed than myself to sell the idea to the community.

Well done on your tutorials and articles.

Trevor

03.06.2009

@Willabee-
Yes, if this demo was something that I would use in production, I would pull the JS into an external file.

Yes, I would treat radio buttons the same way as checkboxes, but the point of the demo is that it flexible and easy to be changed.

I definitely think maxlengths are useful if you need to restrict length, but you should still check on the server side as well. I absolutely hate tabindex though. It always ends up messing up the tab order when you start adding new fields, or adding other forms to a page. I try and avoid it.

That project sounds interesting, but I don’t think I would have the time to devote to that.

balaganek

04.09.2009

Hey,

can you explain me why did you make ol.forms float: left? I don’t understand that. I’m a rather CSS newbie but wouldn’t it be better to avoid floats and make it for example overflow: hidden?

Trevor

04.09.2009

@balaganek-
It’s not completely necessary, but since the labels are floated, I floated the list items. Then to contain the floated list items, I floated the ordered list.

balaganek

04.10.2009

Thanks for the response. I thought that you don’t have to float container in order to have floated elements inside it. I was using “overflow: hidden” in order to have everything working correctly. I found working with such floated containers much harder. But as i said previously, i’m not a CSS expert, but for now i haven’t found any disadantages of using my approach.

Trevor

04.10.2009

@balaganek-
Floating a container is one way to contain the floats., and using overflow: hidden is another.

The problem with using overflow: hidden in this situation is, what happens if you want to use absolute positioning to show an error message next to the form field, and it breaks out of the container. If you were using overflow: hidden, you can’t push it outside of the container.

syndrael

02.10.2010

Great tutorial.. easy and detailed..
Best regards from Paris,France under the snow..
——-

Too late, comments are closed!

Don’t worry, you can email me or contact me on Twitter.