Alecaddd | Designer Developer Dreamer

Web Design and Development, Digital Painting, Music and Life

  • Who
  • What
  • Uses
  • Blog
Posted in Tutorials

WriteToMe: a simple email form with PHP and jQuery

featured

 

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:

  • 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

/*
	---------------------------------------------------
	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

 

Posted on December 26, 2014December 26, 2014
  • Twitter
  • Facebook
  • Reddit
  • Pinterest
How I got hired thanks to my blog
Web Development ShowReel – May 2015

Recent Posts

  • Thunderbird Development – Implementing Customizable Shortcuts September 4, 2024
  • Featured image of windows loval web development setupEasiest Windows Development Setup – WordPress, Laravel, NodeJS in 5 minutes May 15, 2022
  • We are the generation of forced nostalgia March 27, 2022
  • Working on Thunderbird. UX and UI challenges of an Open Source project. February 23, 2021
  • XP-Pen Artist 12 Pro Review and International Giveaway! January 14, 2020

Podcast Episodes

  • Episode 44 – The Nerds are back! May 18, 2020
  • Episode 43 – This is awkward…getting settled into your new role May 12, 2019
  • Episode 42 – We need to talk about Gutenberg Development Workflow… February 24, 2019
  • Episode 41 – You’re not a real developer, unless… February 18, 2019

Categories

  • Coding 1
  • Reviews 8
  • Tutorials 12
  • Updates 18