Posted in jQuery | Posted on 02-09-2009 | 46,853 views
Last week I was performing a security review on a charity's web site (because that's how I roll, I'm nice like that) when I noticed they were using a jQuery plugin for forms validation. This was something I had not yet looked at in the jQuery world so I decided I'd take a closer look this weekend. I have to admit that I'm in awe over what I've found. I've avoided client side form validation for a long time (and I'll talk more about why in a minute) but I see no reason to do so anymore. What follows is a brief introduction into what I've learned. I'll be blogging a bit more on this topic later in the week with some more advanced examples.
First, the nitty gritty. All of the code I'm going to talk about today uses a jQuery plugin. This is not something built into the main jQuery file. The plugin has the wonderfully simple name of Validation and may be found here. It was written by a member of the jQuery team, Joern Zaefferer. Documentation may be found here.
The plugin works in two main ways. You can either use various CSS classes and attributes in your HTML to 'flag' fields for validation or use JavaScript to explicitly define validation rules and behavior. I'll be focusing on the 'inline' version for this blog entry. It is the simplest and most direct way to do validation, but only scratches the surface of what the plugin supports. (And to be clear - even in the 'inline' version I'll use a bit of JavaScript.) Let me start with a sample (ripped from the jQuery docs) to give you an idea of how easy this plugin makes validation.
2
3<head>
4<script src="/jquery/jquery.js"></script>
5<script src="/jquery/jquery.validate.js"></script>
6<script>
7$(document).ready(function(){
8 $("#commentForm").validate();
9});
10</script>
11</head>
12
13<body>
14
15<form id="commentForm" method="get" action="">
16 <fieldset>
17 <legend>A simple comment form with submit validation and default messages</legend>
18 <p>
19 <label for="cname">Name</label>
20 <em>*</em><input id="cname" name="name" size="25" class="required" />
21 </p>
22 <p>
23 <label for="cemail">E-Mail</label>
24 <em>*</em><input id="cemail" name="email" size="25" class="required email" />
25 </p>
26 <p>
27 <label for="ccomment">Your comment</label>
28 <em>*</em><textarea id="ccomment" name="comment" cols="22" class="required"></textarea>
29 </p>
30 <p>
31 <input class="submit" type="submit" value="Submit"/>
32 </p>
33 </fieldset>
34 </form>
35
36
37</body>
38</html>
So going down the code line by line, ignoring HTML we don't care about, make note that we include the plugin right after we load the main jQuery library. The script block will contain the only line of JavaScript we need for the entire validation:
This means: "Find the form with the id commentForm and turn on validation."
And that's it. Seriously. I mean, they couldn't make that any simpler unless they walked over to your house, wrote the code for you while simultaneously pouring you a cup of coffee. How does the validation work? Notice that each of my form fields has a class attribute. The name and comment form fields each have a class of required while email has a class of required and email. You can see this in action here: http://www.coldfusionjedi.com/demos/jqueryvalidation/test0.html
If you load up the form and immediately hit the submit button, notice that all 3 form fields will have an error message added next to them. Enter a name and a comment, but then try putting in a bad email address. Notice how the error changes? That's slick. As you can probably guess, you could change the error messages but in my tests with simple inline validation, the error messages simply just plain worked and worked well!
So if 'email' flags a field as an email address, you can probably guess that there are other options as well. I'll throw in my one main complaint here and that is the documentation doesn't clearly talk about how to work with this inline version of the validation. I asked about this on the jQuery listserv and this is what I found. If you view the list of built in methods (http://docs.jquery.com/Plugins/Validation#List_of_built-in_Validation_methods), you can translate each of these built in methods to an 'inline' version by following these rules:
a) If the validation method takes no arguments, simply add it to the class attribute.
b) If the validation method takes an argument, add it as an argument to the tag itself.
In English, what does that mean? Well for simple checks like url, date, you can do this:
2<label for="cbd">Birthday</label>
3<em>*</em><input id="cbd" name="cbd" size="25" class="required date" />
4</p>
5<p>
6<label for="curl">URL</label>
7<em> </em><input id="curl" name="url" size="25" class="url" value="" />
8</p>
I've added two new form fields. One for birthdate and one for URL. The date validation worked surprisingly well. If there is one thing I've learned about JavaScript and dates is... well, I hate it. Period. So I was pleasantly surprised to see it nicely handle "April 8, 1973" as well as "4/8/73" and "4/8/1973". Also note that my URL field does not have required set. This means that I can leave the field blank, but if I type anything in, it has to be a valid URL.
But what about validation methods that take arguments? You can add these to your HTML as additional attributes. So for example:
2<label for="cname">Name</label>
3<em>*</em><input id="cname" name="name" size="25" class="required" minlength="2" />
4</p>
5<p>
6<label for="cage">Age</label>
7<em>*</em><input id="cage" name="cage" size="4" class="required number" min="1" max="100" />
8</p>
In the first field above I used minlength to specify that the name must be at least two characters. In the second field I specified a min and max value for age. Remember how I said the built in error messages were nicely done? When you try the demo (URL coming up in a second) notice that the error message will correctly handle input both below 1 and over 100.
Let's look at another quick example:
2<label for="cfu">Bio Upload</label>
3<em>*</em><input type="file" id="cfu" name="cfu" size="25" class="required" value="" accept="pdf" />
4</p>
This creates a required file upload field where only PDFs are allowed. Obviously it can only check the extension, but as I said, pretty darn easy, isn't it?
You can view this demo here: http://www.coldfusionjedi.com/demos/jqueryvalidation/test1.html
Note too that the errors are styled a bit nicer. I added this to the HTML:
2label.error { float: none; color: red; padding-left: .5em; vertical-align: top; font-weight:bold}
3</style>
I found this in a demo (by this I mean the use of label.error) but I don't remember seeing it in the documentation anywhere.
So... what next? As I mentioned earlier in the blog entry there is a lot more that this plugin supports if you are up to writing a bit more JavaScript. I've got a few more blog entries in mind over the week so please stay tuned. Finally, let me get on my soapbox for a minute. This stuff is all very cool. It's all very simple. It just plain works. You must remember, however, that no amount of client side validation can replace server side validation. I can disable JavaScript in Firefox with a few mouse clicks so you must always ensure you do proper server side validation before even considering adding client side validation. (If folks want, I can show an example of that as well.)


The date validation isn't bullet proof. Try entering 5/22//52 and it doesn't catch it. Believe me that's a very common error in a date field.
setTimeout(function() {
doSomething();
}, 500);
using the setTimeOut option for each validation in the document.
And Dojo is geared for internationalization, and accessibility.
jQuery is getting there, but I love how dojo made it a huge part of the whole deal.
Probably un-needed in the grand scheme of things, I mean, who really needs multiple languages or accessibility? -- But still, I really think it's swell that Dojo made it an important part of their overall approach.
I just saw a blog post on using jQuery to do server-side validation, which looked interesting. If I come across it again I'll send you the link.
Sorry to keep saying "check out dojo", but I keep seeing people sorta re-inventing the wheel-- while leaving some pretty cool spokes off, as it were.
Guess I'm a fanboy of sorts. Heh. I'm down with that.
:-)
I have to agree with Rick, though: the date validation code in this Validation plugin has room for improvement. It failed my favorite date validation test, which is to see if it tags a non-existent date like Feb. 31 as being wrong. That's something a date picker can prevent merely by not giving you the option of picking that date.
Check this out: http://dojotoolkit.org/forum/dojo-and-html-doctype...
Basically, you can use pure JS or CSS if you're concerned with html validation, whatever the JS library.
Depends on how you want things to degrade and whatnot, too.
@ToddSharp: Did I really? Hmmm, isn't that interesting. I'll have to include a link to the wishlist in the next example. ;)
@Denny: DOJO - I can only learn one thing at a time! ;)
@BrianS: Yep, when I was playing with the UI stuff last week or so, I did see the date picker and thought it was rather nice!
I've used Spry's validation tools a little--I like how simple they are, but the amount of markup (and JS includes) required is a bit overwhelming.
I like that this spits out error messages for you--the less typing, the better :)
Hopefully, Spry will steal some of the nice things here and implement them into future releases.
Any way to set custom patterns? For instance, I'm working on a single field form that accepts either a 13 digit number or a 8 digit number. Anything else should be invalid. I've just started playing with jquery in the last week and I find it pretty cool.
I've slipped the class fields into my code generator that builds my forms for a given database table. Then all I've got to do is create my $(document).ready()
Very unobtrusive. I should have guessed this was built by a jQuery team member since it is so 'elegant'.
By the way, your post form won't allow for email addresses with a '+' in it - as if that was invalid. I'll have to check and see if THIS plugin will allow it...
@Paul Sturm: Sorry about that. That is a bug fixed in BlogCFC, but my blog install here is a bit out of date.
Extending jQuery validation:
http://tinyurl.com/5onkka
You'll be wanting to look at the regular expression stuff.
Something like this:
$.validator.addMethod('eightOr13', function (value) {
return /^((\d{13})|(\d{8}))$/.test(value);
}, 'Please enter a valid 8 or 13 digit code.');
I still like dojo's approach better tho. :-)P
@Ray: time is an interesting concept. :-)
Luckily, it's all open source so it was an easy addition.
Come up with the preso/demo ;-)
I prefer to use tha alternate method: if you also add the jQuery metadata plugin - http://plugins.jquery.com/project/metadata - you can do something like this:
<p>
<label for="cage">Age</label>
<em>*</em><input id="cage" name="cage" size="4" class="required number {min:1,max:100}" />
</p>
About Gabe's issue in IE6 and document-ready: Have you tried placing your doSomething()-call in a script block just before the closing body-tag?
About localization: The plugin has english as the default language, but is bundled with 17 localizations (mostly contributions from other users).
About accessibility: The plugin uses labels with the correct for-attribute for the error messages, so a screenreader should be able to link the error-label to the associated input.
@Brad Wood: Would you mind sharing your addition?
Unfortunately, most of the examples I find are with php and asp so I have to integrate it into cfm myself. Your ideas are very, very helpful, Ray. My latest adventure is to place a file upload form into a jquery accordion panel that stays in the panel after the upload. It's slow going.
The inline validation seems convenient, but maybe not the most unobtrusive approach. If you think of validation as just another aspect of your application, having the validation rules (class="required" minlength="2") applied directly to the form fields isn't the best separation of concerns.
Granted the pragmatic side of me says this lightweight approach probably fits the need for most developers out there and that a more AOP-style validation engine, either database or XML driven, might be a little overkill.
We currently use the EXTJS package, and while its not stoopid large, its not tiny.
Why can't there be a single package that does every freaking thing........... <piss and moan>
Both PHP and CF support reading files, but they certainly do it different ways. Ditto for the JS frameworks out there.
It is important that you (and by you I mean either you as the sole developer or you and your organization) find the library that enables you to be the most productive. (Frankly I think client download isn't a huge concern. jQuery has compressed versions and I'm sure EXT does as well. Spry does for sure.)
@Everyone/anyone:
jQuery seems to be going for the bottom-up on localization and accessibility, vs. dojo's top-down. I can appreciate the logic, and I feel that the only way it will work, is if devs guide other devs in "the right way" to code this stuff.
Make a big deal about being multi-lingual, and accessible (in general)!
Or maybe don't. Evolution will take care of all of it, in the end, come to think of it. Eh. =]
@Ray & Niel: Dojo has had /everything/ and /anything/ that I've needed to do with JS. Just amazing stuff. The build system, various tools for working with other languages... just far-out, wonderful stuff. I like how things are organized (the sources)... the list goes on and on.
Course, javascript is a pretty freaky language in and of itself, and just about any "helper" lib can do nifty things for it, so... guess whatever floats your boat, as with anything.
Swell to hear that localization and accessibility have been thought of with this jQuery validation plugin. I don't know why I like that so much, but I do. =]
That means we can't rely on <cfform> for any of its JS or AJAX-related features. Bummer!
After shopping around, I found jQuery to be a nice alternative. These demos make it seem even more enticing. In a way, the examples here are better than what one would get from <cfform> because you don't get the annoying alert() window. The error messages are much more friendly.
You can use the "scriptsrc" cfform attribute to specify a different directory to pull the JS and stuff from, bypassing a call to your IT department.
HIH
I couldn't figure out from the documentation for the validation plug in how to make a text input field required when they choose a specific drop down box.
Basically I have a "how did you hear from us" drop down. If they choose "OTHER" I need to make the "OTHER" input field required.
If this is in the documentation, I'm sure I missed it for sure. Maybe a link to the page?
Thanks!
http://www.coldfusionjedi.com/index.cfm/2009/2/16/...
Iam new to ASP.NET and jquery, i implemented the above written code in my .aspx pages. I have added the .js files onto the page. Now, whenever i run my project i get an error "jquery is undefined". I really don't know how to move forward. any help!!!
<script src="/js/jquery.js"></script>
Is that the right path to the jquery folder? Does Firebug confirm the script has loaded?
$("#form1").validate(); is undefined.
The code which i have written is as follows:
<script src="jquery.js" type="text/javascript"></script>
<script src="jquery.validate.pack.js" type="text/javascript"></script>
<script src="jquery.validate.js" type="text/javascript"></script>
<script src="jquery.form.js" type="text/javascript"></script>
<script src="jquery-1.3.2.min.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function() {
$("#form1").validate();
});
</script>
and then the form validations in the body tag. What more i have to do??
Regards
Prabhjot Singh
key is undefined
[Break on this error] var parts = key.split(".");
This error is pointing to jquery.js file. When i open the description i get this
data()()jquery.js (line 1355)
data()(input#cname.required)jquery.v...lidate.js (line 795)
rules()(input#cname.required)jquery.v...lidate.js (line 771)
(?)()()jquery.v...lidate.js (line 609)
(?)()()jquery.js (line 353)
grep()(Object length=3 0=input#cname.required, function(), undefined)jquery.js (line 1129)
filter()(function())jquery.js (line 352)
refresh()()jquery.v...lidate.js (line 605)
validator()(undefined, form#form1)jquery.v...lidate.js (line 397)
validate()(undefined)jquery.v...lidate.js (line 245)
(?)()()index.aspx (line 16)
(?)()()jquery.js (line 2905)
each()([function(), function()], function(), undefined)jquery.js (line 690)
ready()()jquery.js (line 2904)
(?)()()jquery.js (line 2929)
[Break on this error] var parts = key.split(".");
I have written down the same code as u. What next should i do??
Regards
prabhjot singh
Everything is working just fine, Thanks for your help. Iam so happy
i have to validate my first name and the last name field in such a way that no alpha-numeric character is entered. The "required" is working fine, but what about the other thing like i have mentioned. I tried by putting in a new function in jquery.js but it did'nt work. So, is it possible??
Regards
Prabhjot singh
http://docs.jquery.com/Plugins/Authoring
http://pastie.org/447779
-TR
Thanks for sharing your ideas. But like others I also love the Spry validation of dreamweaver because of its ease of use and modifications. I can simple add the spry validation using mouse clicks. And I found a way for using the Spry validation of dreamweaver with jQuery ajax form submit here: http://abcoder.com/javascript/using-dreamweaver-sp...
I could not get the required class to work on a file upload. I was using the recommended mime-type for the accept attribute "image/jpeg". When I changed it to accept="jpg" ... problem solved.
Thanks for posting this tutorial. I look forward in seeing more on jQuery from you.
I'm with you on learning one thing at a time. Unfortunately, for me I have a job offer and they are using DoJo...I understand that the 2 frameworks can will not conflict.
Thanks again Ray.
Dojo: Yep, you can set up jQuery to use another 'root' instead of $. That way it won't conflict.
I've had a few stints with the plugin myself -- But I was wondering if you knew of a way to 'highlight' the fieldset/div/table row (or another parent element) instead of adding a label with the error message inside. Know what I mean? So if the name textbox is empty, that entire div is highlighted, instead of the label action.
That's what I'm working on now. Would love your thoughts on it if possible.
Fantastic site btw -- keep up the great work :)
~Jay
http://jquery.bassistance.de/validate/demo/
The demo with 'custom display' is close to what you want I believe.
But I am new at this, that said:
Why do you prepend the fields with the letter "c"?
Is it a convention or part of the api for the validation plugin?
It seems to be kind of sporadic to the untrained eye.
I've attached your code below as an example.
<pre>
<p>
<label for="cname">Name</label>
<em>*</em><input id="cname" name="name" size="25" class="required" minlength="2" />
</p>
<p>
<label for="cage">Age</label>
<em>*</em><input id="cage" name="cage" size="4" class="required number" min="1" max="100" />
</p>
</pre>
Thanks!
So... ignore/don't worry about/etc. :)
<code>
<script>
$().ready(function() {
$("#form).validate({
rules: {
name: "required",
number: {
required: true,
number: true
},
messages { ... }
}
});
});
</script>
</code>
This way you can also add custom error messages for each field in the same consistent way you define the rules for each field (would've been even nicer if they simply added a message attribute to the rules options).
Thanks for the great article!
I'd like to put the labels on the right of text input fields and display the error messages under input fields.
Tried almost anything, but to no avail. :-(
Any help is greatly appreciated!
You're right and I know how to do it by simply displacing the <label> and <input> tags. But the problem is the label gets completely disordered when the error message appears.
For simple form, with two or three fields, i think is better to write yourself the validation code than including the entery Validation plugin.
Something like: if ($('elm').val().length == 0) { alert('message'); }
Or:
if($('elm').is(':checked')) { alert('message'); }
[Add Comment] [Subscribe to Comments]