Having a way for users to contact you is essential, and a contact form is the standard method of enabling this. A stylish form that displays messages to the user, seamlessly sends the mail in the background, and displays a suitable response makes the site stand out.
Here we create a form, with a few standard fields, and link it to form processing functionality in WordPress via it’s built-in Ajax functionality connected via jQuery.
Form Building
First we create the form. We’re using bootstrap for the styling in this case, though if you want to create your own form using a different css framework, such as foundation, or your own styling, it’s only the form fields names & id’s which are important. We’ll be using a simple form with basic fields: name, subject, email and message. These will all be required fields.
Contact Field Template
The contact page is using a custom page template: page_contact.php. Linked to this is a template partial containing the form – and other page styling not shown here:
<?php
/**
* Template for displaying the contact page
*/
/**
Template Name: Contact
*/
?>
<?php get_header(); ?>
<main role="main" id="wrapper" class="page-content">
<?php if ( have_posts() ) : the_post(); ?>
<?php get_template_part( 'templates/page', 'contact' ); ?>
<?php endif; ?>
</main>
<?php get_footer(); ?>
And the template partial – page-contact.php, stored in a folder named ‘templates’:
<?php
/**
* Template for the contact page form data
* - Used within the loop
*/
?>
<?php
// Get address defaults
$c_addr_1 = get_post_meta(get_the_ID(), '_contact_addr_1', true);
$c_addr_2 = get_post_meta(get_the_ID(), '_contact_addr_2', true);
$c_addr = trim( $c_addr_1 . ' ' . $c_addr_2 );
// Get phone default
$c_tel = get_post_meta(get_the_ID(), '_contact_phone', true);
// Get email default
$c_email = get_post_meta(get_the_ID(), '_contact_email', true);
// Get text defaults
$c_text_1 = get_post_meta(get_the_ID(), '_contact_text_1', true);
$c_text_2 = get_post_meta(get_the_ID(), '_contact_text_2', true);
?>
<section id="cform" class="container-fluid red-lt">
<div class="row">
<div class="col-md-12 textpadding">
<form name="sentMessage" class="well" id="contactForm" novalidate>
<div class="control-group">
<div class="controls">
<input type="text" class="form-control input-lg" placeholder="Full Name" id="fullname" name="fullname" required data-validation-required-message="Enter Name" />
</div>
</div>
<div class="control-group">
<div class="controls">
<input type="email" class="form-control" placeholder="Email" id="email" name="email" required data-validation-required-message="Enter Email" />
</div>
</div>
<div class="control-group">
<div class="controls">
<input type="text" class="form-control" placeholder="Subject" id="subject" name="subject" required data-validation-required-message="Enter Subject" />
</div>
</div>
<div class="control-group">
<div class="controls">
<textarea rows="8" cols="95" class="form-control" placeholder="Message" id="message" name="message" required data-validation-required-message="Please enter your message" data-validation-minlength-message="Min 5 characters" maxlength="5999" style="resize:none"></textarea>
</div>
</div>
<input name="action" type="hidden" value="onti_contact" />
<?php wp_nonce_field( 'onti_cf_html', 'onti_cf_nonce' ); ?>
<div id="success"> </div> <!-- For success/fail messages -->
<button type="submit" class="btn btn-primary pull-right">Send</button>
</form>
</div>
</div>
</section>
Contact form defaults
You will notice that the template partial retrieves default values for the four main contact form fields in the form $c_xxx. There are numerous ways of getting this data e.g post_meta, user_meta, language files, hard-coded. In this case these are stored in the form of post meta data against the page tied to the contact page. You can use a post-type / meta data plugin to create these if needed e.g ACF. The form element contains the ‘novalidate’ attribute, which set specifies that the form-data should not be validated on submission. This is dealt with elsewhere.
Contact Form Processing
Once submitted – we’ll detail that later – the form is processed via a function which parses the form post data, and returns – errors or success messages – via json. This is placed in a contact.php file in an includes folder. It is loaded by adding it as an include via your themes function.php file:
// Contact form
require_once( get_template_directory() . '/includes/contact.php' );
And the contact.php file:
<?php
/**
* Contact form functionality
*
* - Used within the loop
*/
function my_contact_form() {
if ( isset( $_POST['my_cf_nonce'] ) && wp_verify_nonce( $_POST['my_cf_nonce'], 'my_cf_html' ) ) {
$to = get_option( 'admin_email' );
$name = sanitize_text_field($_POST['fullname']);
$email = sanitize_email($_POST['email']);
$subject = sanitize_text_field($_POST['subject']);
$message = wp_kses_data($_POST['message']);
$headers[] = 'From: ' . $name . ' <' . $email . '>';
$sent = wp_mail( $to, $subject, $message, $headers );
if ( $sent) {
$r = array( 'name' => $name,
'email' => $email,
'to' => $to,
'subject' => $subject,
'message' => $message);
wp_send_json_success($r);
} else {
$r = array('message' => 'Mail Error' );
wp_send_json_error($r);
}
}
$r = array('message' => 'Validate Error' );
wp_send_json_error($r);
}
// WordPress Ajax
add_action( 'wp_ajax_my_contact', 'my_contact_form' );
add_action( 'wp_ajax_nopriv_my_contact', 'my_contact_form' );
Going through this, the function first tests that the form is valid and the nonce field matched the one supplied by the form. This is recommended security that all ajax calls through WordPress – and any other remote form processing – should follow. Stops remote hijacking of the mailer. If it’s not validated then the default return error message is sent via json. The form then processes the name, email, subject & message fields and if the mail is sent successfully then a success message is sent, otherwise an error message is sent. Success and failure use the built-in WordPress functions. The email is sent to the site admin email address, taken from the site settings.
Notice there is no validation of the field content here. It’s possible to do it this way and use jQuery to dynamically amend a per-field error message, but in this case we’re using pre-submission validation via the jqBootstrapValidation plugin, as shown later.
At the foot of the file are a couple of lines which connect the submission with WordPress built-in Ajax functionality. The action has the form wp_ajax_{xxx} and wp_ajax_nopriv_{xxx}. These are for logged in and non-logged in users respectively. In this case both behave the same and call the function stated by the second argument: ‘my_contact_form’. The xxx is the action that WordPress uses to link the ajax call to the right functionality. Here that is ‘my_contact’. It is used in the jQuery Ajax request, below.
Form Validation
To do the form validation we’re going to need some extra functionality. This is provided by the jqBootstrapValidation plugin and some custom jQuery. This is included by loading some scripts via the WordPress enqueue functionality. The plugin file and associated contact.js file are stored in a theme js folder:
// Load other conditional scripts
function my_contact_scripts() {
// Page template - contact form - google map
if ( is_page_template( 'page_contact.php' ) ) {
// Contact form
wp_register_script('contact-validate', get_template_directory_uri() .'/js/jqBootstrapValidation.js', array('jquery'), NULL, true );
wp_register_script('contact-form', get_template_directory_uri() .'/js/contact.js', array('jquery'), NULL, true );
wp_enqueue_script('contact-validate');
wp_enqueue_script('contact-form');
$args = array( 'ajax_url' => admin_url( 'admin-ajax.php' ) );
wp_localize_script('contact-form', 'contact', $args);
}
}
// Load the scripts
add_action('wp_enqueue_scripts', 'my_contact_scripts');
The contact.js file contains the custom jQuery functionality to interact with the form fields and display error messages when the form is incorrectly submitted. It also contains the ajax post call and callback functions to display success or failure messages:
/* Jquery Validation using jqBootstrapValidation */
(function($,c) {
$("input,textarea").jqBootstrapValidation({
preventSubmit: true,
submitError: function($form, event, errors) {
alert( 'submit Error');
},
submitSuccess: function($form, event) {
event.preventDefault();
$.post( c.ajax_url, $("#contactForm :input").serialize(), function( data ) {
if ( data.success ) {
// Success message
$('#success').html("<div class='alert alert-success'>");
$('#success > .alert-success').html("")
.append( "</button>");
$('#success > .alert-success')
.append("<strong>Thanks! Your message has been sent.</strong>");
$('#success > .alert-success')
.append('</div>');
} else {
// Fail message
$('#success').html("<div class='alert alert-danger'>");
$('#success > .alert-danger').html("")
.append( "</button>");
$('#success > .alert-danger').append("WHOA! <strong>Sorry "+firstName+", it seems my email system is having a moment...</strong> Please email me directly to <a href='mailto:me@example.com'>xxxx</a>.");
$('#success > .alert-danger').append('</div>');
}
//clear all fields
$('#contactForm').trigger("reset");
}, "json");
},
filter: function() {
return $(this).is(":visible");
},
});
$("a[data-toggle=\"tab\"]").click(function(e) {
e.preventDefault();
$(this).tab("show");
});
})(jQuery, contact);
/*When clicking on Full hide fail/success boxes */
jQuery('#name').focus(function() {
$('#success').html('');
});
It looks like a lot of code, but essentially all it does is associate the jqBootstrapValidation functionality to the input fields. It then tests for attributes such as: required data-validation-required-message=”Enter Name” to test for validation states. If any fail the respective error message is displayed in the html. If validation passes then the ajax call is sent to the WordPress ajax url with all the post data and this is processed by the WordPress function linked to the action, as described above. The returned json success / failure message is then parsed and displayed as required.
Wrapping It Up
Hopefully, this has given you an insight on how WordPress ajax functionality works, and a good example of form processing via Ajax. There’s more than one way of doing it; this is just one of the ways. For example you can tweak this to move the validation from the form to the processing function.