Creating an Ajax contact form - from start to finish

Posted on Friday, June 20th, 2008

In this tutorial I’ll be showing you how to create an Ajax contact form from start to finish. This includes the form HTML/CSS, the JavaScript and the PHP backend.

Obviously the form needs to work when JavaScript is disabled so we’ll have to create a functional PHP solution first, and then enhance it with AJAX. You can see an example of what we’ll achieve here.

I’m sure that most of you are proficient in basic HTML and CSS styling so I won’t spend too long on this bit.

The HTML:

The first thing to do is semantically code the form. We will be using an ordered list to contain the various form elements:

<form action="contact.php" method="post" id="contactform">
	<ol>
		<li>
			<label for="name">Your Name*</label>
			<input id="name" name="name" class="text" />
		</li>
		<li>
			<label for="email">Your Email Address*</label>
			<input id="email" name="email" class="text" />
		</li>
		<li>
			<label for="phone">Your Phone number</label>
			<input id="phone" name="phone" class="text" />
		</li>
		<li>
			<label for="concerning">Message concerning?</label>
			<input id="concerning" name="concerning" class="text" />
		</li>
		<li>
			<label for="message">Your Message*</label>
			<textarea id="message" name="message"></textarea>
		</li>
		<li class="buttons">
			<input type="submit" value="Send" id="submit" />
			<input type="reset" />
		</li>
	</ol>
</form>

The CSS:

Right, now that we’ve got the markup sorted we can move on to the styling. The most usable forms, in my opinion, are the ones which retain a tabular structure since they divide each step of the form into a logical structure. So we’ll try to emulate that with some CSS:

#contactform {
	margin: 0;
	width: 500px;
	background: #CCCCCC;
	padding: 5px;
}
#contactform * {
	font-family: Verdana, Arial, Helvetica, sans-serif;
	font-size: 12px;
}
#contactform ol {
	margin: 0;
	padding: 0;
	list-style: none;
}
#contactform li {
	margin: 0 0 5px 0;
	padding: 10px;
	background: #FBFAEC;
}
#contactform li.buttons {
	margin: 0;
	overflow: hidden;
}
#contactform label {
	margin: 0;
	width: 190px;
	display: block;
	float: left;
	padding: 5px 10px 2px 0;
}
#contactform input.text {
	width: 274px;
	border: 1px solid #D4D4D4;
	padding: 2px;
	margin: 0;
}
#contactform textarea {
	width: 274px;
	border: 1px solid #D4D4D4;
	padding: 2px;
	height: 80px;
	background: #FDFDF7;
}
#contactform li.buttons input {
	padding: 4px;
	float: right;
	margin: 0 0 0 5px;
	width: auto;
}

You can see what it currently looks like here.

Now we want to write the PHP which will process the form. There are various PHP classes available for form processing (search on Google if you’re interested), but for the sake of this tutorial I will go through how to write a simple script: (Intermediate knowledge of PHP necessary)

First, create a file called contact.php and open it with your favourite text editor.

The PHP Script:

UPDATE: Tatu has put together a more graceful script (speedier and with more advanced error-checking capabilities) - You can see it here - It should work fine with the AJAX in this tutorial.[[LINK NOT WORKING ANYMORE]]

We need to check that the request for this page is always a POST request. If it isn’t then the script should not continue:

if(!$_POST) exit;

Let’s define some imortant variables:

Array of form values (name attribute):

$values = array ('name','email','phone','concerning','message');

Specify those which are required in another array:

$required = array('name','email','message');

The address the email will be sent to:

$your_email = "james@example.com";

The subject of the email:

$email_subject = "New Message";

The first line of the email:

$email_content = "new message:\n";

Now we need to loop through the form values and add them to $email_content, but the script will discontinue (exit) if any of the values specified in the $required array are empty.

for( $i = 0 ; $i < count( $values ) ; ++$i ) {
	for( $c = 0 ; $c < count( $required ) ; ++$c ) {
		if( $values[$i]==$required[$c] ) {
			echo $required[$x];
			if( empty($_POST[$values[$i]]) ) { echo 'PLEASE FILL IN REQUIRED FIELDS'; exit; }
		}
	}
	$email_content .= $values[$i].': '.$_POST[$values[$i]]."\n";
}

Now we can send the email using PHP’s mail() command:

if(mail($your_email,$email_subject,$email_content)) {
	echo 'Message sent!'; 
} else {
	echo 'ERROR!';
}

So, the script will show either “ERROR” or “Message Sent!” dependent on whether or not the mail() command is successful. If the mail() command does not work for you then it’s likely to a be a server configuration issue (you should contact your host and have a “chat”).

So the very small PHP script is finished: (beginners: don’t forget to wrap the script in <?php & ?>

if(!$_POST) exit;
 
$values = array ('name','email','phone','concerning','message');
$required = array('name','email','message');
 
$your_email = "james@example.com";
$email_subject = "New Message";
$email_content = "new message:\n";
 
for( $i = 0 ; $i < count( $values ) ; ++$i ) {
	for( $c = 0 ; $c < count( $required ) ; ++$c ) {
		if( $values[$i]==$required[$c] ) {
			echo $required[$x];
			if( empty($_POST[$values[$i]]) ) { echo 'PLEASE FILL IN REQUIRED FIELDS'; exit; }
		}
	}
	$email_content .= $values[$i].': '.$_POST[$values[$i]]."\n";
}
 
if(mail($your_email,$email_subject,$email_content)) {
	echo 'Message sent!'; 
} else {
	echo 'ERROR!';
}

It’s not perfect though. You could add some additional validation rules and to be honest the script could be a little more secure (perhaps by checking the referrer etc.) but it’ll do for now.

AJAXIFY:

Now we can add some AJAX’y coolness! Obviously we’ll be using jQuery here so you’ll need to download and link to it in your document:

<script type="text/javascript" src="jquery.js"></script>

Let’s start:

Check if document is ready for DOM manipulation:

jQuery(document).ready(function(){ // stuff here

Attach function to onsubmit event of the form:

$('#contactform').submit(function(){ // stuff here

Within the submit function: (when the form is submitted)

Assign script (action attribute) url to a variable:

var action = $(this).attr('action'); // i.e. contact.php

Add loader (animated gif) image and disable submit button:

$('#submit')
	.before('<img src="ajax-loader.gif" class="loader" />')
	.attr('disabled','disabled');

Now let’s post the data and handle the returned data: (using jQuery post method.

$.post(action,
	{ // ALL THE DATA:
	name: $('#name').val(),
	email: $('#email').val(),
	phone: $('#phone').val(),
	concerning: $('#concerning').val(),
	message: $('#message').val()
	},
	function(data){ // WHEN DATA HAS BEEN POSTED:
		$('#contactform #submit').attr('disabled',''); // Enable used of submit button again
		$('.response').remove(); // Remove any previous instances of the ".reponse" message
		$('#contactform').before('<span class="response">'+data+'</span>'); // Add response to document
		$('.response').slideDown(); // Cool effect
		$('#contactform img.loader').fadeOut(500,function(){$(this).remove()}); // Fade that loader img out and then remove it!
		if(data=='Message sent!') $('#contactform').slideUp(); // Slide up the form IF the response is positive
	}
);

Don’t forget to return false! Otherwise the data will be submitted to contact.php normally.

return false;

Entire script:

jQuery(document).ready(function(){
 
	$('#contactform').submit(function(){
 
		var action = $(this).attr('action');
 
		$('#submit')
			.before('<img src="ajax-loader.gif" class="loader" />')
			.attr('disabled','disabled');
 
		$.post(action, { 
			name: $('#name').val(),
			email: $('#email').val(),
			phone: $('#phone').val(),
			concerning: $('#concerning').val(),
			message: $('#message').val()
		},
			function(data){
				$('#contactform #submit').attr('disabled','');
				$('.response').remove();
				$('#contactform').before('<span class="response">'+data+'</span>');
				$('.response').slideDown();
				$('#contactform img.loader').fadeOut(500,function(){$(this).remove()});
				if(data=='Message sent!') $('#contactform').slideUp();
			}
		);
 
		return false;
 
	});
 
});

SEE A FINISHED DEMO HERE!

If you have any questions about anything then feel free to leave a comment below and I’ll reply as soon as possible! :)

39 Responses to “Creating an Ajax contact form - from start to finish”

  1. Gravatar #1 :: Jeffrey Way Says:

    Hey, James. What’s up? I’m not a PHP developer, so I didn’t read over the whole tutorial. But, when reviewing the demo, I noticed that there doesn’t seem to be any validation. If I click “Send” without filling in the textboxes, it tells me “Message Sent”. It would only take a few lines of code to perform some client-side validation.

  2. Gravatar #2 :: James Says:

    Jeffrey is right about validation - I really should’ve added it! … Not to worry - I just added it to the PHP backend script so the validation will work even when JS is disabled! :)

  3. Gravatar #3 :: Scriptdaemon Says:

    You should move the count() functions outside the for loops so it doesn’t call the function every time.

  4. Gravatar #4 :: Scriptdaemon Says:

    Better yet, now that I think about it, I would rewrite the lines as:

    for( $i = 0, $count = count( $values ) ; $i < $count ; ++$i ) {

    This keeps the $count variable in the scope of the for loop.

  5. Gravatar #5 :: Tatu Says:

    A nice tut but the PHP portion is a bit bulky, so I devised an improved version that can be found here:

    http://rafb.net/p/WTkyoM41.html LINK NOT WORKING ANYMORE

    There is really no need to use several nested for-loops when one foreach can handle the required fields. I also added an more intuitive error system that allows several errors to be presented to the user instead of exiting on the first error encountered - saves the user some time when they can fix all the issues at once. An easy way to validate individual fields with regular expressions is also presented.

    Hope you like it and feel free to copy parts of the code if you wish.

  6. Gravatar #6 :: James Says:

    Thanks Scriptdaemon and Tatu for ur great suggestions… I’m not primarily a PHP guy so I kinda assumed there was a better way of doing it. ;)

    Tatu, very nice, I’m gonna link to ur script at the top of the PHP section of the tut (as an alternative). Email validation! … nice! :)

  7. Gravatar #7 :: Lindsey Says:

    Sweet, the validation works now for the required fields that aren’t filled in, but the only other problem I see is that even if you enter just text in for the email address (ex: “ddd”) it still sends. Is there a way to easily evaluate the email input field to make sure a valid (or at least correctly formed) email address has been typed in?

  8. Gravatar #8 :: James Says:

    @Lindsey - Yep sure, in fact Tatu (another commenter) has put together a script with that included ( http://rafb.net/p/WTkyoM41.html - LINK NOT WORKING ANYMORE). Look on line 18:

    $error[] = preg_match('/\b[A-Z0-9._%-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b/i', $_POST['email']) ? '' : 'INVALID EMAIL ADDRESS';

    I was going to work some advanced validation into the tut but I guess I never got round to it…

  9. Gravatar #9 :: Jenny Says:

    this is a great tutorial. i’m doing it now, and I hope I do it right. :)

  10. Gravatar #10 :: Wim Says:

    Sorry, but the script doesn’t even check if the entered email address is in fact an email address.

  11. Gravatar #11 :: James Says:

    @Wim - That’s easy enough to add though, Tatu gives an example here: http://rafb.net/p/WTkyoM41.html (LINK NOT WORKING ANYMORE)

    $error[] = preg_match('/\b[A-Z0-9._%-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b/i', $_POST['email']) ? '' : 'INVALID EMAIL ADDRESS';
  12. Gravatar #12 :: Tatu Says:

    Oh, great. The link that I provided seemed to expire in 24 hours and cannot be found anymore and I don’t have the script saved. Hope you got it somewhere and are able to relink it somewhere else if you want. Thanks for noticing my message, anyhow.

  13. Gravatar #13 :: James Says:

    @Tatu - Darn! … I didn’t save it!

    I remember a bit about the script (like there being a new error array - for collection of errors) but other than that I don’t remember much…

    I guess there’s no way to retrieve it… :( :(

  14. Gravatar #14 :: Milk and Company Says:

    Nice post… I like it like Milk and Company

  15. Gravatar #15 :: John Says:

    Nice tutorial! I’m confused why you’re using an ordered list if you’re going for semantics. Form elements should be within a fieldset tag if that’s what your intentions were, using labels and styles to get your desired effects. Just creates a lot of overhead in your html and in your css.

  16. Gravatar #16 :: John Says:

    Also, here is a function for checking the email address:

    function check_email_address($email) {
    		if (!ereg("^[^@]{1,64}@[^@]{1,255}$", $email)) {
    			return false;
    		}
    		$email_array = explode("@", $email);
    		$local_array = explode(".", $email_array[0]);
    		for ($i = 0; $i < sizeof($local_array); $i++) {
    			if (!ereg("^(([A-Za-z0-9!#$%&'*+/=?^_`{|}~-][A-Za-z0-9!#$%&'*+/=?^_`{|}~\.-]{0,63})|(\"[^(\\|\")]{0,62}\"))$", $local_array[$i])) {
    				return false;
    			}
    		}
    		if (!ereg("^\[?[0-9\.]+\]?$", $email_array[1])) {
    			$domain_array = explode(".", $email_array[1]);
    			if (sizeof($domain_array) < 2) {
    				return false;
    			}
    			for ($i = 0; $i < sizeof($domain_array); $i++) {
    				if (!ereg("^(([A-Za-z0-9][A-Za-z0-9-]{0,61}[A-Za-z0-9])|([A-Za-z0-9]+))$", $domain_array[$i])) {
    					return false;
    				}
    			}
    		}
    		return true;
    }
  17. Gravatar #17 :: Zod_42 Says:

    I’m pretty new to php & ajax. Is there a set number of fields you can use on this form? When I try to put more than 7 it won’t let me submit it. Any help on this would be greatly appreciated.

  18. Gravatar #18 :: Rusty Says:

    Other than server side validation (which was already covered) the only thing I didn’t like about the form was that it just left you hanging there after you received the “Message sent.” test. I would have changed the if statement in the ajax section into something like this:

    if(data=="Message sent!"){
      $('#contactform').slideUp();	
      setTimeout('window.location="http://www.google.com/"',5000);
      document.getElementById("redstate").innerHTML = "You are about to be redirected to www.google.com!";
    }
  19. Gravatar #19 :: Rusty Says:

    I edited the form and posted it at my website. You can check out the change I made at:

    http://www.firecrackerwebdesign.com/contactform.html

  20. Gravatar #20 :: Manny Says:

    Hi, everything seems to be work ok, but for some reason i receive the contact form message the sender is always “APACHE”. What can be the problem?

    Thanks for this great script!

  21. Gravatar #21 :: James Says:

    @Zod - Did you fill the $values php array with the names of all your fields?

    @Manny - You can specify various header information when using the mail() method, for example you could have it so the email is “from” the person who filled out the form:

    mail($your_email,$email_subject,$email_content,'From: '.$email)

    @Rusty - Redirecting is definitely an option but may not be suitable for all apps.

    @John - An ordered list originally seemed to be the best way to do it because it offered a sufficient amount of elements to style plus seemed quite a semantic way of doing it - A user has to enter data progressively through the form (usually in an order)… it makes sense to me. Nick Rigby’s solution seems to be similiar: http://www.alistapart.com/articles/prettyaccessibleforms

  22. Gravatar #22 :: Thomas Milburn Says:

    Another great tutorial!
    Can I just suggest a bit of php simplification. Instead of:

    for( $i = 0 ; $i &lt; count( $values ) ; ++$i ) {
    	for( $c = 0 ; $c &lt; count( $required ) ; ++$c ) {
    		if( $values[$i]==$required[$c] ) {
    			echo $required[$x]; //what does this do? and where did $x come from?
    			if( empty($_POST[$values[$i]]) ) { echo 'PLEASE FILL IN REQUIRED FIELDS'; exit; }
    		}
    	}
    	$email_content .= $values[$i].': '.$_POST[$values[$i]]."\n";
    }

    use:

    foreach($values as $value){
      if(in_array($value,$required)){
        if( empty($_POST[$value]) ) { echo 'PLEASE FILL IN REQUIRED FIELDS'; exit; }
        $email_content .= $value.': '.$_POST[$value]."\n";
      }
    }

    @John That’s a great email validating function. Using preg_match would make it slightly faster though.

  23. Gravatar #23 :: Matt Says:

    The CSS included in the file at the top doesn’t match the one in the preview, not a big deal, but if anyone didn’t notice, the response section is missing, making it not as pretty.

  24. Gravatar #24 :: Yaro Says:

    Hello! I’m just wondering how to get it work with other chasets than UTF-8?

  25. Gravatar #25 :: Laurnet.M Says:

    Hi Guys,
    Thanks for this great stuff.
    I have 2 questions :
    - I put this form in a popup, and I would like to close it when it sent. Do you know how to do that ? With Jquery in function(data) ?

    - I would like to send some data in this popup(contactform.html). Actually I call it with a classic href.
    Do you know how to send data into this popup and displayed in the contact form ?

    Many thanks

  26. Gravatar #26 :: ghprod Says:

    Wow .. great tutz :D

    thnx

  27. Gravatar #27 :: splitline Says:

    Great work. Anymore AJAX tutorials with php… Why use Ajax instead of Javascript though.. I’m a little confused between the two.. Has anyone checked out google’s version of javascript??

    Thanks

  28. Gravatar #28 :: febstahr Says:

    is there an updated form out there?
    i mean with a secure php file like email checker and so?

    thx

  29. Gravatar #29 :: Dan Says:

    ^ Looking for that too.

  30. Gravatar #30 :: Josh (vancouver computers) Says:

    There is no Ajax secure form that I know of, but I am working to make one, including validations.

  31. Gravatar #31 :: Mark Says:

    Thanx for this form. I’d like to add the suggestions in comments #8 and #16 but i don’t really know where to put them. Can somebody help me ? Thanx a lot.

  32. Gravatar #32 :: Detected Says:

    I love this script ;)
    Only problem is, the contactform doesn’t slideUp and disappear when I use the script (when Message is send succesfully) ..
    I even tried copy-pasting your source code from /examples/ajax-contact-form.html !
    What could be the problem?

    Thnx in advance :)

  33. Gravatar #33 :: Detected Says:

    Ok! I’m sorry! I found the error! How stupid of me :D
    I changed the message ‘Message sent!’ in the php
    and forgot it in the javascript “if(data==’Message sent!’)”

  34. Gravatar #34 :: zazu Says:

    Just don’t know where i’ve to put it in. maybe someone can help me :)

    ps: sorry for my bad english :)

    function check_email_address($email) {
    		if (!ereg("^[^@]{1,64}@[^@]{1,255}$", $email)) {
    			return false;
    		}
    		$email_array = explode("@", $email);
    		$local_array = explode(".", $email_array[0]);
    		for ($i = 0; $i &lt; sizeof($local_array); $i++) {
    			if (!ereg("^(([A-Za-z0-9!#$%&amp;'*+/=?^_`{|}~-][A-Za-z0-9!#$%&amp;'*+/=?^_`{|}~\.-]{0,63})|(\"[^(\\|\")]{0,62}\"))$", $local_array[$i])) {
    				return false;
    			}
    		}
    		if (!ereg("^\[?[0-9\.]+\]?$", $email_array[1])) {
    			$domain_array = explode(".", $email_array[1]);
    			if (sizeof($domain_array) &lt; 2) {
    				return false;
    			}
    			for ($i = 0; $i &lt; sizeof($domain_array); $i++) {
    				if (!ereg("^(([A-Za-z0-9][A-Za-z0-9-]{0,61}[A-Za-z0-9])|([A-Za-z0-9]+))$", $domain_array[$i])) {
    					return false;
    				}
    			}
    		}
    		return true;
    }
  35. Gravatar #35 :: newto0 Says:

    Hi,

    How open the form again after the mail sending with link, without reload.

    Thanks

  36. Gravatar #36 :: ozzie Says:

    Is this site still monitored. If so I have a problem to where the contact.html opens contact.php as a html page. Or I get an error that says “firefox can’t find file C:/..contact.php. Both html and php are in same directory. I have code for both if you want it to see what I am doing wrong. Email me at ozzie212@yahoo.com

  37. Gravatar #37 :: mtsandeep Says:

    i am not that good in php, How can i make the message(at top, error message like “fill in your details”) also to disappear when i click the reset button.
    Now only the content inside the text box gets removed when we click the reset button.

  38. Gravatar #38 :: kailoon Says:

    So, is the whole tutorial is working?

    Is it possible to show a sample with “select” in used?

    I want to have some extra fields added when I select a specific value/item. Is it easy to do?

    The validation work?

  39. Gravatar #39 :: nemenem Says:

    How open the form again after the mail sending with link, without reload.
    thanks for this comment.

Leave a Response

Allowed elements include: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <em> <i> <q cite=""> <strike> <strong>.
Please wrap any multi-line code snippets in the <pre> element. (Characters are automatically escaped this way) - also you can specify the language within the lang attribute, e.g. <pre lang="php">echo 'hello';</pre>. (Available languages: "php", "javascript", "html4strict")