Spruce interactive


Simple AJAX Contact Form

Posted: May 5th, 2013

Author: Ben Heller


, , , ,

 Details

** UPDATED 6/11/2013 **

In making the transition to becoming a PHP developer (I began with HTML/CSS), there were occasions on which I needed some simple AJAX functionality. Despite the almost overwhelming buzz surrounding AJAX, I didn’t really understand how to implement it in a simple situation. So, I was left copying existing tutorials with bloated code samples and far too much jQuery wizardry. This quick tutorial is for PHP and jQuery beginners who need the foundation for an AJAX contact form that can be easily understood and subsequently modified. We’ve assumed that the tutorial will be implemented inside WordPress, but it’s just as easily used in any other framework, or even plain PHP.

Let’s start with the HTML. We want to capture the user’s name, e-mail address, and a message.

<form id="contact" method="post">
    <label>Your Name</label><br />
    <input name="name" id="name" type="text">  
    <label>Your Email</label><br />
    <input name="email" id="email" type="text">  
    <label>Your Question</label><br />
    <textarea rows="10" name="text" id="text" ></textarea>
  <input type="submit" value="Send Message" name="submit">
  <p class="success" style="display:none">Your message has been sent successfully.</p>
  <p class="error" style="display:none">E-mail must be valid and message must be longer than 100 characters.</p>

Next we build the jQuery that handles our form submission. Put this outside $(document).ready() or any other event functions.

// Contact Form
  var name = $("#name").val();
  var email = $("#email").val();
  var text = $("#text").val();
  var dataString = 'name=' + name + '&email=' + email + '&text=' + text;
  function isValidEmail(emailAddress) {
    var pattern = new RegExp(/^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i);
    return pattern.test(emailAddress);

  if (isValidEmail(email) && (text.length > 100) && (name.length > 1)){
    type: "POST",
    url: "/wp-content/themes/YOURTHEMENAME/functions.php",
    data: dataString,
    success: function(){
  } else{

  return false;

First we’re hooking into the form submit function for #contact and preventing the default submit action. Next we’re setting variables for the value of each form field by element ID. Once the variables are set, we create a dataString that puts all these values together, which we can then pass to the AJAX function. In this function, we’re submitting the form using the dataString we constructed above, and passing this to a PHP file that sends the actual e-mail. Remember to replace YOURTHEMENAME in the code above with the path to your theme directory. If you’re not using WordPress, this just needs to point to the file that will be running your PHP, wherever that’s located. This AJAX function is only run if the e-mail address entered is valid, the message length is greater than 100 characters, and the name length is greater than 1 character. You can fiddle with these variables to suit your particular spam protection needs. If there’s a problem with validation, our request is never sent to the PHP function and we instead fade in a mean ol’ error message.

The PHP code looks like this:

// Email Submit
// Note: filter_var() requires PHP >= 5.2.0
if ( isset($_POST['email']) && isset($_POST['name']) && isset($_POST['text']) && filter_var($_POST['email'], FILTER_VALIDATE_EMAIL) ) {

  // detect & prevent header injections
  $test = "/(content-type|bcc:|cc:|to:)/i";
  foreach ( $_POST as $key => $val ) {
    if ( preg_match( $test, $val ) ) {
  //send email
  mail( "YOURADDRESS", "Contact Form: ".$_POST['name'], $_POST['text'], "From:" . $_POST['email'] );

This receives the form data via $_POST and uses the PHP mail() function to send the actual e-mail. The ISSET checks allow you to place this code in a global functions.php (which is customary for WordPress themes) without calling it on each page reload. Remember to replace YOURADDRESS appropriately–you can also make use of variables within each mail field to customize the subject and text of your e-mail. Once this is sent, it triggers the success function in our jQuery code, at which point we fade in our success message to let the user know that their email has been sent successfully. If you’re feeling comfortable with everything up to this point, you can continue on to add better error handling and various animations to this form, but as it stands this is a solid example of basic AJAX functionality. As a matter of fact, it’s almost exactly what we use here at Spruce! Feel free to get in touch if you have any questions, or would like our assistance implementing this on your own site.

  • Pramodgurav1

    Thank you sir..

  • http://www.eattherich.co.uk/ Darryl Young

    Great article. This is just what I was looking for!

  • Kreativejuices


    Totally agree with you on the whole ” bloated code overkill” when all you wanted to know was how to achieve the actual ajax part itself. I’ve used this as a basis to create a joomla module. Works a treat! 

  • http://www.facebook.com/twstnshout Charlie Carver

    Isn’t that “if” function wrong? There seems to be an extra “});”

    • spruceit

      Right you are! Don’t know how that one slipped in ;-)

      We’ve fixed the example above accordingly.

  • khanz

    I am confused. According to
    http://www.w3schools.com/php/php_mail.asp $to, $subject, $message are compulsory. From your code it seems that variable can be named arbitrarily & in any order?

    • spruceit

      Hi Khanz,

      You’re correct that the parameters of “to, subject, and from” are mandatory, and should be used with the mail function in this order: mail(to, subject, message, headers, parameters). However, you can name them anything you need! The variables themselves don’t need to be called $to, $from, $subject, $message like in the example you cited. That’s the beauty of variables–they can take any name you wish. In our example, the mail function is being given: mail(“RECIPIENT ADDRESS”, “SUBJECT”, $text, “From:” . $email), with RECIPIENT ADDRESS and SUBJECT being filler text informing our readers that these two parameters can be anything they wish. On our actual site, RECIPIENT ADDRESS is a local Spruce email address that receives the message, and SUBJECT is something like “Incoming contact form submission from USER EMAIL”.

      We had previously labeled “RECIPIENT ADDRESS” as “SENDER ADDRESS” which I agree is terribly misleading. All apologies there–this is the TO field, and is the email address that will receive the message.

      • khanz

        Thnx, can you also refer me to some other simple php guides on your site. Am an engineering student

  • rconserta

    Can you show how the error handling would be placed in the jquery code above? I figured out the form, but I can’t get the background color of the name text field to turn a color when nothing is entered.

    • spruceit

      Hi there, no problem–I’ve pasted below the actual submit function we use here on Spruce. Hopefully this will help clarify! It’s very simplistic and just checks to make sure each field isn’t blank, in sequential order, but it gets the job done in most cases.


  • rconserta

    I kinda figured it out, maybe someone wants to make it a little better.

    $(function() { $(“#contact .submit-button”).click(function() { var name = $(“#form_name”).val(); var email = $(“#form_email”).val(); var text = $(“#msg_text”).val(); var dataString = ‘name=’+ name + ‘&email=’ + email + ‘&text=’ + text; if(name==”){ $(‘#form_name’).css({‘background-color’:'#FAFFBD’}); } else { if(email==”){ $(‘#form_email’).css({‘background-color’:'#FAFFBD’}); } else { if(text==”){ $(‘#msg_text’).css({‘background-color’:'#FAFFBD’}); } else { $.ajax({ type: “POST”, url: “email.php”, data: dataString, success: function(){ $(‘.success’).fadeIn(1000); } }); } } } return false; });

  • Erik

    You Da Man! I have been looking for this exact thing and google was giving me the run-around. THANKS!

  • http://www.facebook.com/TheArthurius Michal Dominik Timko

    Thank you very much for this article. Can you help me? I have one problem with this. Always only empty message comes from $name to my mail. Where is problem?

    • spruceit

      Hi there,

      It sounds like maybe one of the IDs for a field name isn’t being passed to either the JS or PHP–if you’d like to send us an email (info@spruce.it) with your code we’ll gladly take a look and see what’s up!

      • http://www.facebook.com/TheArthurius Michal Dominik Timko

        thank you :) email sended

        • spruceit

          E-mail replied to! But, in case someone else has the same problem, I’ll summarize here as well. If you’re embedding the PHP in the same page as the HTML and jQuery (rather than using a mail.php file), you’ll need to change the url: in the AJAX call to url: location.href

  • שגיב אמרי

    i see no reason for this “if($_POST){“… the file would only be called by this javascript right?

    • spruceit

      Ostensibly, yes, but better safe than sorry! :-)

  • Omer Bonfil

    Thank you!! exactly what i wanted.

  • Steven

    Thanks for making this simple and completely understandable.

    I’ve managed to make everything work correctly including form validation via your previous comment. Also, I’ve managed to reset the form when a user submits via the setTimeout function.

    However, what I CANNOT seem to do is remove any css attributes that are set during validation.

    I am using this in a slide down modal box (bootstrap). When a user hits Contact in the main nav, the modal slides down with the contact form (this desc. is for interaction perspective only, not something that has to be debugged).

    On submitting, the form uses setTimeout to show the .success message then slides the modal back out of view. Clicking Contact again shows the modal/form, while cleared or reset, still shows the validation error css highlights. Within the .ajax .success function, I have tried to reset the css. No luck as of yet.

    • spruceit

      Hey Steven,

      You’re definitely taking the right approach by resetting the CSS in the AJAX success function. If you’d feel comfortable emailing us at team@spruce.it with the code you’re using for the form, we’d be happy to take a look at it. Thanks!

      • Steven

        I sent the js file. did you see?

        • spruceit

          Hi Steven,

          We took a look at the JS–while we’re not privy everything that’s going on with the site, it looks like you’re having trouble with the $(this).animate method of changing the background-color. It may be that you’re not loading jQuery UI, which I believe is required to use animate in conjunction with color changes (see: http://api.jquery.com/animate/). This is corroborated by the fact that your click functions on name, e-mail, and text (respectively on lines 12, 20, and 28) don’t work either. If you change these to $(this).css(‘background-color’,'#FFFFFF’); then you’ll be in business. Or, you can load up jQuery UI to use the animate method. Hope this helps!

          • Steven

            Yes, yes. I should’ve seen that. Works perfectly, now. So, where can I send a few pints of some fine Ale?

          • spruceit

            Haha, thanks Steven! No need to shower us in ale–just spread the word that we’re nice dudes and we’ll call it even :-)

  • Guest

    trying to add the custom field telephone – keep getting the error

    Port error: Could not establish connection. Receiving end does not exist.

    in console – any ideas?

  • Guest

    looks like that’s for an extension… derp… it still isn’t working though

    else if(telephone==”){


    $(“#form_telephone”).click(function() {

    $(this).animate({‘background-color’:'#FFFFFF’}, 500);




    • spruceit

      Hi there–is it possible you didn’t add your telephone field to the data string in order to pass it through to the PHP?

      var telephone = $(“#form_telephone”).val();
      var dataString = ‘name=’+ name + ‘&email=’ + email + ‘&text=’ + text + ‘&telephone=’ + telephone;

  • Justin

    Isn’t refreshing, and email doesn’t show in my inbox as being from them, rather the email address is part of the subject, and the email it sends from is my server’s mail handler… help?

    • spruceit

      Hi Justin–it’s tough to troubleshoot without looking at the full set of code, but if you want to drop us a line via our contact form we’d be happy to take a look for you!

      • Justin

        Right… will do.

  • http://goo.gl/YoLRg Husy


  • http://schoberg.net/ Jesse

    Is it just me or this is WIDE open to header injection? Nothing is being sanitized in the php script. What’s stopping spammers from doing something like this… http://jsbin.com/avicem/1/edit

    • spruceit

      @schoberg:disqus, you are absolutely right — thanks for pointing that out! We’ve updated the PHP portion of the script to check for valid e-mails, just a note that the validation method we chose requires PHP to be at version 5.2.0 or greater.

      • http://schoberg.net/ Jesse

        I’m not sure that will be enough. I would suggest sanitizing all the fields. For example you could put this above the mail() function:

        // Header Injection Stopper. Insert just before mail() function
        foreach($_REQUEST as $fields => $value) if(eregi(“TO:”, $value) || eregi(“CC:”, $value) || eregi(“CCO:”, $value) || eregi(“Content-Type”, $value)) exit(“ERROR: Please don’t use the following in your message: ‘TO:’, ‘CC:’, ‘CCO:’ or ‘Content-Type’.”);

        • spruceit

          Duly noted. I like your suggestion, however I propose an alternate solution so if for some reason people NEED to use the TO:/CC: in the body of their e-mail, they can. Instead of simply checking for header keywords, we can strip all POST requests of newline characters. What do you think?

          • http://schoberg.net/ Jesse

            Well I will say I am not an expert on this, I feel like I recall reading there were multiple ways to create line breaks and that method was a bit harder to make bulletproof. The above is a quick and dirty that does work. and the chances of someone needing to put BCC: in to their email are pretty next to zero. But yes there are cleaner ways for sure. I would just do a bit of research before I posted an alternate option to be sure it’s bulletproof.

          • spruceit

            You’re right, it’s better to err on the side of caution. I’ve updated the main post with something more in line with your suggestion.

            Also, here are some fun links for interested readers:

  • Keisa

    I am trying to follow this tutorial but I’m new to all of this…is it possible for you to explain where each set of code needs to be located within a Page template please?

  • Guest

    Looking at the jQuery, I believe some nefarious code has been inserted.

    • spruceit

      Could you explain what line you believe to be troublesome and why? Thanks!

  • curVV


  • Josh Herder

    Thanks a lot! This had me up and running in no time!

  • Jon

    This doesn’t seem to validate the sender’s email address at all.
    I have the code working and just entered “1″ into the email field and the email was still successfully sent.
    Also, should I wish to launch this script from a simple “Contact Me” link in my nav bar how would I go about it?

  • Andrew

    Thank you so much for this. You should put a “Buy me a beer” button on here :) Cheers.

  • Aaron

    Hi I tired to email you but it keep getting delivery errors. Let me know how I can send you an email. Thanks!

  • Christian Cruz

    I have several input fields (Name, Last name, email, phone, message). In your example we can send the message in the email body but how do I get the rest of the info to be displayed in the email as well?

    Last name:

    UPDATE (I got it to work, nevermind!!!)

  • Guest

    Thank you so much! I’ve been looking for a simple solution on how to do this for months!!!


  • drekerr

    I’ve been looking for a simple explanation on how to do this for months!!! THANK YOU!!!

  • pankaj

    Good Job man..it works..short and simple

  • http://www.ostips.co.uk OSTips

    Thanks for the code. BIt of adaption I can see myself using it for many other projects. Sometimes these things need to be broken down and you can worry about fine tuning later.

Talk to Us

Wise men have said, "All good things must come to an end", and so it was with Spruce. We began our first project in 2006, and completed our last in Fall 2013. It's been a wonderful experience, but now it's time to begin the next adventure.

Though...we wouldn't want to leave you hanging, now would we? Here are our top recommendations for world-class branding, design, and web development:

  • Kristina (KJ) Parish and Beam Collective:
    KJ provided the artistic vision that allowed Spruce to succeed, and we still think she's the greatest designer in the entire world. She's started a new collective with some talented developers, and is covering much the same territory as Spruce--design and development all under one roof, with a caliber of service and professionalism seldom seen in this business or any other. If you like what you see on the Spruce site, then definitely get in touch with KJ at kj@beamcollective.com
  • Permanent Art & Design
    Permanent is a nationally recognized design & branding firm out of Minneapolis, MN. They've been a long term collaborator and strategic partner over the past couple years, and we'd recommend them for medium to large sized businesses/organizations who are serious about putting their best foot forward. In addition to design, Permanent offers strategy and marketing services, plus in-house and networked developers. For more information contact Joseph Belk joseph@permanentadg.com
  • Nate Thompson
    A freelancer, Nate reminds us of ourselves back in the day. Smart, communicative, and skilled, Nate is our recommendation for small to medium sized businesses/organizations who have sites built on Wordpress or other open source content management solutions. With a background in both design and development, he's a one-stop shop for most web-related tasks. You can get in touch at nate.a.thompson@gmail.com
  • Kevin DeBernardi
    Designer turned developer, Kevin is embarking on a freelance web career after working as the in-house designer at the Museum of the City of New York. He's adept at translating his design ideas into custom PHP code, and is constantly expanding his technical palette. Kevin's a good choice for projects that don't quite fit into the Wordpress mold, and that would benefit from a consistency of presence and vision from design through execution. Kevin can be reached at kevin@analoglifestyle.com