Hello folks and welcome to another amazing tutorial about the wonderful and enchanted world of the Internet…too much sugar for me today.
Anyway, in this tutorial I’m gonna show you how to create a custom contact form for your website, without using any plugin or external repository. Before starting tho, I think I should answer the question that probably came on your mind…
“Why should I follow this tutorial instead of using some awesome plugin?”
The answer is easy, because you can!
Using pre-made code means that you don’t have the full control of what’s going on under the hood, and the more plugins or external resources you use, the more heavy and hard to maintain your website becomes.
Being able to know what’s going on in your source code can bring a lot of benefits to your website in terms of loading speed and security; but unfortunately not all of us have the knowledge nor the time to build everything by ourselves or study the source code of every plugin we install, so, what could be the solution, or at least a good and not so complicated practice to adopt? For me is to spend some time to create my own code in order to manage the simplest aspects of my website, for example, as in this case, a contact form.
The result
Before starting let’s take a look at the final product, and if you don’t have time to follow this Tutorial, you can also find the link to download the full package from Github.
Demo or Download
As you can see we have a pretty standard form with a bunch of fields. The system detects which one is required and avoid to send the email if one of the fields is empty. The entire process is managed via jQuery post(), an asynchronous method to dialog with a PHPÂ file in our server without reloading the page.
All the animations are meant to entertain a little bit our users, trying to give them a process as less painful as possible (let’s be honest, filling forms is sooo boring)!
HTML
The basic structure of our form, including the initial and final state of our sending process.
<div class="container">
<div class="loading">
<div class="rect1"></div>
<div class="rect2"></div>
<div class="rect3"></div>
<div class="rect4"></div>
<div class="rect5"></div>
<p>Sending..</p>
</div>
<form id="form-send" method="post">
<div class="mail-container">
<div class="mail-content">
<div class="mail-front">
<div class="mail-body">
<div class="input-container required name-input">
<label for="nameUser">Insert your name</label>
<input type="text" id="nameUser" placeholder="your name" class="send-input" required>
</div>
<div class="input-container required email-input">
<label for="emailUser">Insert your email address</label>
<input type="email" id="emailUser" placeholder="your email" class="send-input" required>
</div>
<div class="input-container required msg-input">
<label for="msgUser">How can I help you?</label>
<textarea id="msgUser" rows="3" placeholder="your message" class="send-input" required></textarea>
</div>
</div>
</div>
<div class="mail-back">
<div class="mail-body">
<i class="fa fa-paper-plane fa-5x"></i>
<p class="thanks-title">Thank you!</p>
<p class="thanks-msg">Your email is on its way</p>
<a href="#" class="reload">Send another one</a>
</div>
</div>
</div>
</div>
<div class="input-container">
<input id="foo" type="hidden" value=""><!-- leave this hidden input without values for SPAM prevention -->
<button type="submit" class="btn send"><span>Send</span></button>
</div>
</form>
</div>
Due to the fact that we don’t want to reload the page or change page after the email has been sent, I like to build the HTML structure with all the elements that I’m gonna use during the process. Of course you can always manipulate the DOM with javascript, jQuery or whichever method you like to use, but in cases like this one, where we handle pre-made statuses and a really limited amount of answers, having everything ready to go in your page makes things easier to handle.
Let’s take a look at the code to understand it better:
This div is hidden and contains the loading animation that I’m gonna use during the email sending process. It’s simply a div in an absolute position that, after it appears, it prevents users to edit the form or click multiple times on the sending button while the PHP code is processing the request.
- div.mail-front / div.mail-back
Because I decided to use a flip card animation for my email form, I had to create the front and back div, then simply style them with CSS.
- input type and required attribute
It’s always a good behaviour to specify which input type we’re handling in our forms. A lot of browsers today support the HTML5 input formats and they can handle for us a first step of validation and, in case of mobile devices, request the right keyboard to the operating system. The required attribute also prevents the submission of the form in case one of those fields is empty.
CSS
/*
---------------------------------------------------
FORM ELEMENTS
---------------------------------------------------
*/
label {
display: block;
text-align: center;
margin: 5px 0;
font-weight: 300;
}
input,
textarea {
font-size: 20px;
padding: 10px 12px;
border-radius: 2px;
border: 1px solid #ccc;
color: #ae2525;
outline: none;
max-width: 300px;
width: 100%;
}
textarea {
resize: none;
}
input:focus,
textarea:focus {
border-color: #B15252;
}
input.first-input {
text-align: center;
}
.mail-body label {
text-align: left;
width: 100%;
max-width: 322px;
margin: 5px auto;
}
.required label:after {
content: '*';
color: #C70000;
font-size: 15px;
font-weight: 600;
padding-left: 4px;
}
.name-input label:before,
.email-input label:before,
.msg-input label:before {
content: '';
font-size: 21px;
color: #D1D1D1;
padding-right: 8px;
font-family: FontAwesome;
font-style: normal;
font-weight: normal;
line-height: 1;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.name-input label:before {
content: "\f183";
}
.email-input label:before {
content: "\f0e0";
}
.msg-input label:before {
content: "\f040";
}
.btn {
font-size: 20px;
font-weight: 400;
color: #fff;
padding: 10px 12px;
border: none;
border-radius: 2px;
background-color: #B15252;
}
.btn:hover, .btn:focus {
background-color: #ae2525;
}
.btn, input, a {
-webkit-transition: all .2s ease-in-out;
-moz-transition: all .2s ease-in-out;
-o-transition: all .2s ease-in-out;
transition: all .2s ease-in-out;
}
.btn-init {
width: 100%;
max-width: 200px;
}
.sending {
border-radius: 50%;
max-width: 43px;
}
.send {
width: 100%;
max-width: 100%;
}
/*
---------------------------------------------------
RESPONSE ELEMENTS
---------------------------------------------------
*/
.error > input {
border-color: #B15252;
}
.error:after {
content: 'Invalid value';
display: inline-block;
padding: 3px 7px;
color: #fff;
background: #F35959;
border-radius: 2px;
position: relative;
float: right;
margin-top: 9px;
margin-left: -100px;
-webkit-animation: fadein .6s ease-in-out;
animation: fadein .6s ease-in-out;
}
/*
---------------------------------------------------
FORM CONTAINER
---------------------------------------------------
*/
.container {
width: 100%;
max-width: 570px;
margin: 40px auto;
position: relative;
}
.input-container {
text-align: center;
}
p.description {
font-weight: 300;
font-size: 17px;
text-align: center;
line-height: 1.3em;
margin: 40px 0;
color: #727272;
}
/*
---------------------------------------------------
MAIL FORM CONTAINER
---------------------------------------------------
*/
.mail-container {
-webkit-perspective: 1000;
-moz-perspective: 1000;
-o-perspective: 1000;
perspective: 1000;
display: none;
-moz-transform: perspective(1000px);
-moz-transform-style: preserve-3d;
}
.mail-container.hover .mail-content {
transform: rotateY(180deg);
}
.mail-container, .mail-front, .mail-back {
width: 100%;
}
.mail-content {
transition: 0.6s;/* change the flip speed */
transform-style: preserve-3d;
position: relative;
margin-bottom: 30px;
-moz-transform: perspective(1000px);
-moz-transform-style: preserve-3d;
}
.mail-front, .mail-back {
-webkit-backface-visibility: hidden;
-moz-backface-visibility: hidden;
-o-backface-visibility: hidden;
backface-visibility: hidden;
-webkit-transition: 0.6s;
-webkit-transform-style: preserve-3d;
-moz-transition: 0.6s;
-moz-transform-style: preserve-3d;
-o-transition: 0.6s;
-o-transform-style: preserve-3d;
-ms-transition: 0.6s;
-ms-transform-style: preserve-3d;
transition: 0.6s;
transform-style: preserve-3d;
background: #fff;
border-radius: 2px;
box-shadow: 0 1px 0 rgba(0, 0, 0, 0.1);
position: absolute;
top: 0;
left: 0;
}
.mail-front {
z-index: 2;
-webkit-transform: rotateY(0deg);
}
.mail-back {
transform: rotateY(180deg);
text-align: center;
}
/*
---------------------------------------------------
MAIL CONTENT
---------------------------------------------------
*/
.mail-back .fa {
margin: 25px 0;
color: #CA5353;
}
p.thanks-title {
font-size: 35px;
font-weight: 200;
color: #B15252;
margin: 0;
text-shadow: 0 1px 1px #fff;
}
p.thanks-msg {
margin: 0 0 40px;
}
.reload {
display: inline-block;
font-size: 15px;
font-weight: 300;
padding: 4px 5px;
border-radius: 2px;
}
.reload:hover {
color: #fff;
background: #B15252;
}
.mail-body {
padding: 20px;
margin-bottom: 5px;
}
.mail-body .input-container {
margin-bottom: 20px;
}
/*
---------------------------------------------------
LOADING PANEL
---------------------------------------------------
*/
.loading {
width: 100%;
height: 100%;
text-align: center;
font-size: 10px;
position: absolute;
background: rgba(0, 0, 0, 0.65);
display: none;
z-index: 9999;
color: #fff;
}
.loading > p {
font-size: 30px; font-weight: 200;
}
.loading > div {
background-color: #fff;
height: 30px;
width: 3px;
margin-top: 215px;
display: inline-block;
}
/*
---------------------------------------------------
ANIMATIONS
---------------------------------------------------
*/
@-webkit-keyframes fadein {
0% { opacity: 0; }
100% { opacity: 1; }
}
.loading > div {
-webkit-animation: stretchdelay 1.2s infinite ease-in-out;
animation: stretchdelay 1.2s infinite ease-in-out;
}
.loading .rect2 {
-webkit-animation-delay: -1.1s;
animation-delay: -1.1s;
}
.loading .rect3 {
-webkit-animation-delay: -1.0s;
animation-delay: -1.0s;
}
.loading .rect4 {
-webkit-animation-delay: -0.9s;
animation-delay: -0.9s;
}
.loading .rect5 {
-webkit-animation-delay: -0.8s;
animation-delay: -0.8s;
}
@-webkit-keyframes stretchdelay {
0%, 40%, 100% { -webkit-transform: scaleY(0.4) }
20% { -webkit-transform: scaleY(1.0) }
}
@keyframes stretchdelay {
0%, 40%, 100% { transform: scaleY(0.4); -webkit-transform: scaleY(0.4); }
20% { transform: scaleY(1.0); -webkit-transform: scaleY(1.0); }
}
Nothing complicated here. The CSS is pretty linear and simple to understand if you have a basic knowledge of the styling process. Anyway, just as a little advice, I suggest you to be organize and meticulous while you apply your style. Always try to separate the styles in groups based on their use, include cross-browsing declarations and keep it as clean as possible. Working with huge files will be easier and less confusing for other people.
JAVASCRIPT
jQuery( document ).ready(function( $ ) {
$("#form-send").submit(function(e) {
e.preventDefault(); /* prevent the submission of the form */
/* check if the email format is valid (text@text.ext) */
if( !isValidEmailAddress( $('#emailUser').val() ) ) { $('#emailUser').parent('.required').addClass('error'); } else {
/* remove all the error class in case of a second sending attempt */
$(".required").removeClass('error');
/* show the loading animation */
$('.loading').fadeIn(500);
/* POST ajax call */
$.post("sendmail.php", {
recipient: recipient, /* you can remove this one */
name: $('#nameUser').val(),
email: $('#emailUser').val(),
message: $('#msgUser').val(),
foo: $('#foo').val(),
rand: Math.random()
},
function(response) {
if (response == 1) { /* positive response, mail sent */
$('.loading').fadeOut(400, function(){ /* hide the loading animation */
$('.mail-container').addClass('hover'); /* flip the mail container */
});
$('.send').fadeOut(1000); /* hide the button */
/* console.log('ok'); */
} else if(response == 2) { /* missing values, in case the HTML5 required attribute didn't work */
$('.loading').fadeOut(200, function(){
$(".required").each(function() {
/* fore every required class, check if the inside input field is empty and add the error class */
$(this).find('.send-input').filter(function() { return !this.value; }).parent('.required').addClass('error');
});
});
/* console.log('not ok'); */
} else if(response == 3) { /* all the data were ok but the mail() function didn't work */
/* this could depend on your server, handle the answer as you wish */
//console.log('not ok technical');
}
});
return false;
}
});
$( document ).on( 'click' , '.reload' , function() { /* reload the current page */
window.location.reload();
});
});
/* REGEXP pattern to check the written email address */
function isValidEmailAddress(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);
};
The sending process and the asynchronous call to PHP is handled by jQuery thru the $.post() function. The code grabs all the filled inputs and send them to the sendmail.php file.
Before sending the email we can easily check if the insert address is valid. To do so we can use a RegExp function to check the format of the email address with a custom function I called isValidEmailAddress().
PHP
<?php
if($_POST["foo"]==''):/* if the input is not empty it means that a SPAM BOT filled up the entire form */
$name=$_POST["name"];
$email=$_POST["email"];
$message=$_POST["message"];
$recipient=$_POST["recipient"];
if($name=='' || $email=='' || $message==''): echo '2'; exit(); endif; /* if some values is missing, return the error */
/* remove this line */
if($recipient==''): echo '1'; exit(); endif;
/* end removable line */
$date=date("d M Y - h:1 a");
$mail=$recipient; /* change this variable with your email address */
$header = "From: ".$name." <".$email.">" . "\r\n" . "Reply-To: ".$email."" . "\r\n" . "X-Mailer: PHP/" . phpversion();
$header .= "MIME-Version: 1.0\n";
$header .= "Content-Type: text/html; charset=\"iso-8859-1\"\n";
$header .= "Content-Transfer-Encoding: 7bit\n\n";
$msg= '<html><body>New message from Your Website Contact Page.<br /><br /><strong>Name:</strong> '.$name.'<br /><br /><strong>Email:</strong> '.$email.'<br /><br /><strong>Message:</strong> '.nl2br($message).'<br /><br /><strong>Date:</strong> '.$date.'<br /><br /><br /><small><i>From - Your website</i></small></body></html>';
if(@mail("$mail","Your website - Contact form",$msg,$header)): echo '1'; else: echo '3'; endif;/* check if the mail() function succeded */
else: echo '1'; endif;/* send a positive fake return if SPAM is detected */
The final part of our Tutorial is the PHP file. This file receives the variables from the jQuery call and builds the HTML mail with all the necessary informations.
As you can see a further validations is used here, in case something goes wrong the system will return different answers for the jQuery to handle.
Pay attention to the $_POST[“foo”]Â variable, an empty hidden input inside the HTML form. This variable is necessary for a further validation step. Basically Spam Bots or crawlers in search for an open email form to send some useless email, usually fill every input fields they find inside the page. The foo input is empty and hidden so a regular user shouldn’t be able to set a value, but not the Spam Bots, so if the system detects that the variable is not empty, it means the form has been sent by a Bot and not a user.
With this check we can easily skip the entire sending process and just return a fake positive sending message and kick the Bot out of our website. We don’t like bullies!
IN CONCLUSION
This is a really simple email form with limited options and probably is not really suitable for production sites, but hopefully will help beginners or students looking for a starting code to run some tests or have fun. Feel free to use this code wherever you need, fork it on GitHub or write a comment if you find a bug or you want to suggest some improvements.
Demo or Download
..and after all, always remember that Sharing is Caring,
Cheers