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.
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
<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:
- div.loading
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 |
/* --------------------------------------------------- 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<?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.
..and after all, always remember that Sharing is Caring,
Cheers