Welcome to the DjaoDjin Blog!

A place to share experiences in building Software-as-a-Service.

Multiple step form with jQuery validation

by Stephane Robino on Fri, 25 Oct 2013

We will see how we can easily validate a multiple step form thanks to jQuery and jQuery validation plugin. The aim is to validate each step of the form one by one and post a clean form.

Simple Multiple step form

<form class="form-horizontal" action="" method="POST" id="myform">
	<fieldset id="account_information" class="">
		<label for="username" class="col-lg-4 control-label">Username</label>
		<input type="text" class="form-control" id="username" name="username" placeholder="username">
		...
		<p><a class="btn btn-primary" id="next">next</a></p>
	</fieldset>
	
	<fieldset id="personal_information" class="">
		<label for="name" class="col-lg-4 control-label">Name</label>
		<input type="email" class="form-control" id="name" name="name" placeholder="Email">
		...
		<p><a class="btn btn-primary" id="previous" >Previous</a></p>
		<p><input class="btn btn-success" type="submit" value="submit"></p>
	</fieldset>
</form>

Navigation

By using fieldset ids we can create our step with jQuery and CSS

Hide the second fieldset on page load with CSS

#personal_information{
	display:none;
}

And Create the navigation with jQuery

$(document).ready(function(){
	$('#next').click(function(){
		current_fs = $('#account_information');
		next_fs = $('#personal_information');
		next_fs.show();
		current_fs.hide();
	});

 	$('#previous').click(function(){
		current_fs = $('#personal_information');
		next_fs = $('#account_information');
		next_fs.show();
		current_fs.hide();
	});
});

Validation

Integrate jquery validation plugin in our javascript file.

The validate function is called a first time when we click on the next button. By default, validate ignore the hidden fields, so when we click on next we validate only the first part on the form (So it's unuseful to create two forms). After the first step, the submit button will call again the validate function to validate the second part ignoring the first part. We can only call validate function once per form, so we validate all the form thanks to ignore hidden fields feature

	$("#next").click(function(){
		var form = $("#myform");
		form.validate({
			rules: {
				username: {
					required: true,
					minlength: 6,
				},
				........
			},
			messages: {
				username: {
					required: "Username required",
				},
				........
			}
		});
		if (form.valid() == true){
			current_fs = $('#account_information');
			next_fs = $('#personal_information');
			next_fs.show();
			current_fs.hide();
		}
	});

The click event on next button allows to call validate function. What is important is to define rules to validate each field of our form (even for hidden fields). If the first step is valid, the second step is displayed. What is important to know is we can submit the form only if the entire form is valid. By submitting the form validate will be called again to check visible fields and ignoring the valid first step.

Add validation method

There are several existing rules

Example for password confirm

This rules will check if two password fields have the same values

	password : {
		required: true,
	},
	conf_password : {
		required: true,
		equalTo: '#password',
	},

Add custom rules

For example if we want a username with lowercase letters, numbers and dashes: We can setup a new method by calling .addMethod from additional-methods.js (jQuery validation plugin)

Define the rule


	$.validator.addMethod("usernameRegex", function(value, element) {
		return this.optional(element) || /^[a-z0-9-]+$/.test(value);
		}, "Username must contain only letters, numbers, dashes and be lowercase");
	.........

We are able to add this new rule validation on the validate function


	rules: {
		username: {
			required: true,
			minlength: 6,
			usernameRegex: true,
		},
		..........

Full example code

<!DOCTYPE html>
<html>
<head>
	<meta http-equiv="Content-type" content="text/html; charset=utf-8">
	<title>Multiple step form</title>
	<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">
	<script type="text/javascript" src="js/jquery-1.9.0.js"></script>
	<script type="text/javascript" src="http://ajax.aspnetcdn.com/ajax/jquery.validate/1.13.1/jquery.validate.js"></script>
	<script type="text/javascript" src="http://ajax.aspnetcdn.com/ajax/jquery.validate/1.13.1/additional-methods.js"></script>
	<style type="text/css">
		#personal_information,
		#company_information{
			display:none;
		}
	</style>
</head>
<body>
	<div class="container">
		<div class="col-lg-5">
			<form class="form-horizontal" action="" method="POST" id="myform">

				<fieldset id="account_information" class="">
					<legend>Account information</legend>
					<div class="form-group">
						<label for="username" class="col-lg-4 control-label">Username</label>
						<div class="col-lg-8">
							<input type="text" class="form-control" id="username" name="username" placeholder="username">
						</div>
					</div>
					<div class="form-group">
						<label for="password" class="col-lg-4 control-label">Password</label>
						<div class="col-lg-8">
							<input type="password" class="form-control" id="password" name="password" placeholder="Password">
						</div>
					</div>
					<div class="form-group">
						<label for="conf_password" class="col-lg-4 control-label">Confirm password</label>
						<div class="col-lg-8">
							<input type="password" class="form-control" id="conf_password" name="conf_password" placeholder="Password">
						</div>
					</div>
					<p><a class="btn btn-primary next">next</a></p>
				</fieldset>

				<fieldset id="company_information" class="">
					<legend>Account information</legend>
					<div class="form-group">
						<label for="company" class="col-lg-4 control-label">Company</label>
						<div class="col-lg-8">
							<input type="text" class="form-control" id="company" name="company" placeholder="Company">
						</div>
					</div>
					<div class="form-group">
						<label for="url" class="col-lg-4 control-label">Website url</label>
						<div class="col-lg-8">
							<input type="text" class="form-control" id="url" name="url" placeholder="Website url">
						</div>
					</div>
					<p><a class="btn btn-primary next">next</a></p>
				</fieldset>

				<fieldset id="personal_information" class="">
					<legend>Personal information</legend>
					<div class="form-group">
						<label for="name" class="col-lg-4 control-label">Name</label>
						<div class="col-lg-8">
							<input type="text" class="form-control" id="name" name="name" placeholder="Name">
						</div>
					</div>
					<div class="form-group">
						<label for="email" class="col-lg-4 control-label">Email</label>
						<div class="col-lg-8">
							<input type="email" class="form-control" id="email" name="email" placeholder="Email">
						</div>
					</div>
					<p><a class="btn btn-primary" id="previous" >Previous</a></p>
					<p><input class="btn btn-success" type="submit" value="submit"></p>
				</fieldset>

			</form>
		</div>  
	</div>

	<script type="text/javascript">
		$(document).ready(function(){

			// Custom method to validate username
			$.validator.addMethod("usernameRegex", function(value, element) {
				return this.optional(element) || /^[a-zA-Z0-9]*$/i.test(value);
			}, "Username must contain only letters, numbers");

			$(".next").click(function(){
				var form = $("#myform");
				form.validate({
					errorElement: 'span',
					errorClass: 'help-block',
					highlight: function(element, errorClass, validClass) {
						$(element).closest('.form-group').addClass("has-error");
					},
					unhighlight: function(element, errorClass, validClass) {
						$(element).closest('.form-group').removeClass("has-error");
					},
					rules: {
						username: {
							required: true,
							usernameRegex: true,
							minlength: 6,
						},
						password : {
							required: true,
						},
						conf_password : {
							required: true,
							equalTo: '#password',
						},
						company:{
							required: true,
						},
						url:{
							required: true,
						},
						name: {
							required: true,
							minlength: 3,
						},
						email: {
							required: true,
							minlength: 3,
						},
						
					},
					messages: {
						username: {
							required: "Username required",
						},
						password : {
							required: "Password required",
						},
						conf_password : {
							required: "Password required",
							equalTo: "Password don't match",
						},
						name: {
							required: "Name required",
						},
						email: {
							required: "Email required",
						},
					}
				});
				if (form.valid() === true){
					if ($('#account_information').is(":visible")){
						current_fs = $('#account_information');
						next_fs = $('#company_information');
					}else if($('#company_information').is(":visible")){
						current_fs = $('#company_information');
						next_fs = $('#personal_information');
					}
					
					next_fs.show();
					current_fs.hide();
				}
			});

			$('#previous').click(function(){
				if($('#company_information').is(":visible")){
					current_fs = $('#company_information');
					next_fs = $('#account_information');
				}else if ($('#personal_information').is(":visible")){
					current_fs = $('#personal_information');
					next_fs = $('#company_information');
				}
				next_fs.show();
				current_fs.hide();
			});
			
		});
	</script>
</body>
</html>