Очередной блог фрилансера

коротко и полезно о веб-разработке

Menu Close

Красивая форма обратной связи на основе AJAX

Предоставление посетителям сайта простого и надежного средства обратной связи – это важнейшая часть любого веб-проекта. Наиболее простым и часто используемым каналом обратной связи — являются контактные формы. Посмотреть результат можно здесь.

В сегодняшней статье, мы создадим форму обратной связи на аяксе, с использованием современных техник веб-разработки. Для этого мы будем использовать PHP, CSS и jQuery в виде нескольких плагинов. Плагин formValidator будем использовать для проверки введенных значений, а с помощью плагина jQTransform, будем стилизовать текстовые поля и кнопки нашей формы. В дополнение, мы будем использовать класс PHPMailer для отправки на почту введенных данных.

Кроме того, наша форма будет прекрасно работать даже с выключенным JS.

*Для нормальной работы примера, вам понадобится PHP5

Шаг 1 – XHTML

Сначала нам понадобится XHTML-разметка для формы.

demo.php

<div id="main-container">	<!-- The main container element -->

<div id="form-container">	<!-- The form container -->

<h1>Fancy Contact Form</h1>	<!-- Headings -->
<h2>Drop us a line and we will get back to you</h2>

<form id="contact-form" name="contact-form" method="post" action="submit.php">	<!-- The form, sent to submit.php -->

<table width="100%" border="0" cellspacing="0" cellpadding="5">

<tr>
<td width="18%"><label for="name">Name</label></td>	<!-- Text label for the input field -->
<td width="45%"><input type="text" class="validate[required,custom[onlyLetter]]" name="name" id="name" value="<?=$_SESSION['post']['name']?>" /></td>
<!-- We are using session to prevent losing data between page redirects -->

<td width="37%" id="errOffset">&nbsp;</td>
</tr>

<tr>
<td><label for="email">Email</label></td>
<td><input type="text" class="validate[required,custom[email]]" name="email" id="email" value="<?=$_SESSION['post']['email']?>" /></td>
<td>&nbsp;</td>
</tr>

<tr>
<td><label for="subject">Subject</label></td>

<!-- This select is being replaced entirely by the jqtransorm plugin -->

<td><select name="subject" id="subject">
<option value="" selected="selected"> - Choose -</option>
<option value="Question">Question</option>
<option value="Business proposal">Business proposal</option>
<option value="Advertisement">Advertising</option>
<option value="Complaint">Complaint</option>
</select>          </td>
<td>&nbsp;</td>
</tr>

<tr>
<td valign="top"><label for="message">Message</label></td>
<td><textarea name="message" id="message" class="validate[required]" cols="35" rows="5"><?=$_SESSION['post']['message']?></textarea></td>
<td valign="top">&nbsp;</td>
</tr>

<tr>
<td><label for="captcha"><?=$_SESSION['n1']?> + <?=$_SESSION['n2']?> =</label></td>

<!-- A simple captcha math problem -->

<td><input type="text" class="validate[required,custom[onlyNumber]]" name="captcha" id="captcha" /></td>
<td valign="top">&nbsp;</td>
</tr>

<tr>
<td valign="top">&nbsp;</td>
<!-- These input buttons are being replaced with button elements -->
<td colspan="2"><input type="submit" name="button" id="button" value="Submit" />
<input type="reset" name="button2" id="button2" value="Reset" />
<?=$str?>

<!-- $str contains the error string if the form is used with JS disabled -->

<img id="loading" src="img/ajax-load.gif" width="16" height="16" alt="loading" />
<!-- the rotating gif animation, hidden by default -->
</td></tr>

</table>
</form>

<?=$success?>
<!-- The $success variable contains the message that is shown if JS is disabled and the form is submitted successfully -->

</div>
</div>	<!-- closing the containers -->

Как вы могли заметить в строке 8, обрабатывать нашу форму будет файл submit.php. Мы будем использовать этот файл в обоих случаях – как для обычной отправки формы (для посетителей с выключенным JS), так и для аяксовой отправки формы. Это позволит легко обновлять код, без необходимости объединять изменения между файлами.

Далее, мы можете видеть, что мы используем массив $_SESSION для заполнения текстовых полей значениями. Это делается для того, чтобы данные не потерялись во время перенаправления страницы, которое происходит во время отправки формы в submit.php.

Другой важный аспект – это классы, назначенный текстовым полям – classs=”validate[required,custom[onlyLetter]]”. Эти классы используются плагином валидации для определения правил проверки введенных значений для каждого текстового поля. В нашем примере, мы указываем, что поле обязательное, и разрешено вводить только буквы.

Существует множество доступных правил валидации, вы можете узнать о них на домашней странице плагина.

Теперь взгляните, как будет выглядеть наша форма, с применением плагина JQtransform.

1

Шаг 2 – jQuery

МЫ используем два плагина jQuery – jQtransform для стилизации элементов формы, и formValidator для проверки введенных значений на клиентской стороне.

Очень важно помнить, что кроме клиентской стороны, введенные данные всегда нужно проверять на серверной стороне.

Итак, для начала нам нужно подключить все необходимые библиотеки.

demo.php

<link rel="stylesheet" type="text/css" href="jqtransformplugin/jqtransform.css" />
<link rel="stylesheet" type="text/css" href="formValidator/validationEngine.jquery.css" />
<link rel="stylesheet" type="text/css" href="demo.css" />

<?=$css?> <!-- Special CSS rules, created by PHP -->

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript" src="jqtransformplugin/jquery.jqtransform.js"></script>
<script type="text/javascript" src="formValidator/jquery.validationEngine.js"></script>
<script type="text/javascript" src="script.js"></script>

Указанный выше код расположен в секции head, файла demo.php. Сначала мы подключаем CSS-файлы, используемые обоими плагинами, а затем уже библиотеку JQuery и плагины. Возможно, вас заинтересовала строка 5 – это специальный набор CSS-правил, который мы создадим с помощью PHP, для отображения подтверждающего сообщения, как вы увидите в дальнейшем.

Теперь, давайте посмотрим на наш файл script.js.

script.js

$(document).ready(function(){
	/* after the page has finished loading */

	$('#contact-form').jqTransform();
	/* transform the form using the jqtransform plugin */

	$("button").click(function(){

		$(".formError").hide();
		/* hide all the error tooltips */
	});

	var use_ajax=true;
	$.validationEngine.settings={};
	/* initialize the settings object for the formValidation plugin */

	$("#contact-form").validationEngine({	/* create the form validation */
		inlineValidation: false,
		promptPosition: "centerRight",
		success :  function(){use_ajax=true},	/* if everything is OK enable AJAX */
		failure : function(){use_ajax=false}	/* in case of validation failure disable AJAX */
	 })

	$("#contact-form").submit(function(e){

			if(!$('#subject').val().length)
			{
				$.validationEngine.buildPrompt(".jqTransformSelectWrapper","* This field is required","error")
				/* a custom validation tooltip, using the buildPrompt method */

				return false;
			}

			if(use_ajax)
			{
				$('#loading').css('visibility','visible');
				/* show the rotating gif */

				$.post('submit.php',$(this).serialize()+'&ajax=1',
				/* using jQuery's post method to send data */

				function(data){
					if(parseInt(data)==-1)
						$.validationEngine.buildPrompt("#captcha","* Wrong verification number!","error");
						/* if there is an error, build a custom error tooltip for the captcha */
					else
					{
						$("#contact-form").hide('slow').after('<h1>Thank you!</h1>');
						/* show the confirmation message */
					}

					$('#loading').css('visibility','hidden');
					/* hide the rotating gif */
				});
			}

e.preventDefault();	/* stop the default form submit */
})

});

Весь блок скрипта выполняется внутри метода $(document).ready, который гарантирует нам, свое исполнение, только после окончания загрузки страницы.

Далее, мы используем метод jqTransform(), который определен в плагине jqtransform. Он переделывает и стилизует все элементы формы (тестовые поля, кнопки и др.)

Элемент select заменяется набором дивов и ссылок. Это выглядит здорово, однако влечет за собой некоторые проблемы с плагином валидации, в связи с чем, тултип для селекта нам придется сделать вручную.

После этого, в строке 7, вешаем обработчик событий на нажатие кнопки в нижней части формы, с одной строкой кода, которая прячет отображаемые тултипы с сообщениями об ошибках. Это для того, чтобы страница правильно обновлялась, и подсказки не оставались на странице, если пользователь ввел правильные данные.

Далее, мы инициализируем плагин formValidation, с помощью метода validationEngine() и в строке 24 описываем событие формы onsubmit. Пара моментов, на которые стоит обратить внимание здесь – отдельный тултип в строке 28, и дополнительный параметр ajax=1, в строке 39. Этот параметр использует файл submit.php, для того чтобы определить, передан ли запрос с помощью аякса или получен непосредственно через отправку формы.

Также, обратите внимание, что мы используем специальную переменную use_ajax, для предотвращения работы аякса, если форма не прошла проверку.

2

Шаг 3 – CSS

Все наши CSS-правила описаны в файле demo.css

demo.css

body,h1,h2,h3,p,quote,small,form,input,ul,li,ol,label{
	/* reset some of the page elements */
	margin:0px;
	padding:0px;
}

body{
	color:#555555;
	font-size:13px;
	background: url(img/dark_wood_texture.jpg) #282828;
	font-family:Arial, Helvetica, sans-serif;
}

.clear{
	clear:both;
}

#main-container{
	width:400px;
	margin:30px auto;
}

#form-container{
	background-color:#f5f5f5;
	padding:15px;

	/* rounded corners */
	-moz-border-radius:12px;
	-khtml-border-radius: 12px;
	-webkit-border-radius: 12px;
	border-radius:12px;
}

td{
	/* prevent multiline text */
	white-space:nowrap;
}

a, a:visited {
	color:#00BBFF;
	text-decoration:none;
	outline:none;
}

a:hover{
	text-decoration:underline;
}

h1{
	color:#777777;
	font-size:22px;
	font-weight:normal;
	text-transform:uppercase;
	margin-bottom:5px;
}

h2{
	font-weight:normal;
	font-size:10px;

	text-transform:uppercase;

	color:#aaaaaa;
	margin-bottom:15px;

	border-bottom:1px solid #eeeeee;
	margin-bottom:15px;
	padding-bottom:10px;
}

label{
	text-transform:uppercase;
	font-size:10px;
	font-family:Tahoma,Arial,Sans-serif;
}

textarea{
	color:#404040;
	font-family:Arial,Helvetica,sans-serif;
	font-size:12px;
}

td > button{
	/* A special CSS selector that targets non-IE6 browsers */
	text-indent:8px;
}

.error{
	/* this class is used if JS is disabled */
	background-color:#AB0000;
	color:white;
	font-size:10px;
	font-weight:bold;
	margin-top:10px;
	padding:10px;
	text-transform:uppercase;
	width:300px;
}

#loading{
	/* the loading gif is hidden on page load */
	position:relative;
	bottom:9px;
	visibility:hidden;
}

.tutorial-info{
	color:white;
	text-align:center;
	padding:10px;
	margin-top:10px;
}

Обратите внимание на строку 85. Это правило делает кнопки шире, но, к сожалению это плохо выглядит в IE6. Поэтому мы используем специальный CSS-селектор, который игнорируется IE6, но отлично понимают остальные браузеры.

Теперь, все что нам осталось – это PHP.

3

Шаг 4 – PHP

Сначала давайте рассмотрим код в начале файла demo.php

demo.php

session_name("fancyform");
session_start();

$_SESSION['n1'] = rand(1,20);	/* generate the first number */
$_SESSION['n2'] = rand(1,20);	/* then the second */
$_SESSION['expect'] = $_SESSION['n1']+$_SESSION['n2'];	/* the expected result */

/* the code below is used if JS has been disabled by the user */
$str='';
if($_SESSION['errStr'])	/* if submit.php returns an error string in the session array */
{
	$str='<div class="error">'.$_SESSION['errStr'].'</div>';
	unset($_SESSION['errStr']);	/* will be shown only once */
}

$success='';
if($_SESSION['sent'])
{
	$success='<h1>Thank you!</h1>';	/* the success message */

	$css='<style type="text/css">#contact-form{display:none;}</style>';
	/* a special CSS rule that hides our form */

	unset($_SESSION['sent']);
}

Как видите, мы используем массив $_SESSION для хранения двух случайных чисел и ожидаемого результата. Это будет использоваться в дальнейшем, файлом submit.php, для подтверждения того, что капча решена.

Другой интересный момент находится в строке 21, где мы описываем простой CSS-класс. Фактически здесь, мы скрываем форму, и отображаем сообщение об успешной отправке, если у пользователя отключен JS.

submit.php

require "phpmailer/class.phpmailer.php";

session_name("fancyform");	/* starting the session */
session_start();

foreach($_POST as $k=>$v)
{
	/* if magic_quotes is enabled, strip the post array */
	if(ini_get('magic_quotes_gpc'))
	$_POST[$k]=stripslashes($_POST[$k]);

	$_POST[$k]=htmlspecialchars(strip_tags($_POST[$k]));
	/* escape the special chars */
}

$err = array();

/* some error checks */
if(!checkLen('name'))
	$err[]='The name field is too short or empty!';

if(!checkLen('email'))
	$err[]='The email field is too short or empty!';
else if(!checkEmail($_POST['email']))
	$err[]='Your email is not valid!';

if(!checkLen('subject'))
	$err[]='You have not selected a subject!';

if(!checkLen('message'))
	$err[]='The message field is too short or empty!';

/* compare the received captcha code to the one in the session array */
if((int)$_POST['captcha'] != $_SESSION['expect'])
	$err[]='The captcha code is wrong!';

/* if there are errors */
if(count($err))
{
	/* if the form was submitted via AJAX */
	if($_POST['ajax'])
	{
		echo '-1';
	}

	/* else fill the SESSION array and redirect back to the form */
	else if($_SERVER['HTTP_REFERER'])
	{
		$_SESSION['errStr'] = implode('<br />',$err);
		$_SESSION['post']=$_POST;

		header('Location: '.$_SERVER['HTTP_REFERER']);
	}

	exit;
}

/* the email body */
$msg=
'Name:	'.$_POST['name'].'<br />
Email:	'.$_POST['email'].'<br />
IP:	'.$_SERVER['REMOTE_ADDR'].'<br /><br />

Message:<br /><br />

'.nl2br($_POST['message']).'

';

$mail = new PHPMailer();	/* using PHPMailer */
$mail->IsMail();

$mail->AddReplyTo($_POST['email'], $_POST['name']);
$mail->AddAddress($emailAddress);
$mail->SetFrom($_POST['email'], $_POST['name']);
$mail->Subject = "A new ".mb_strtolower($_POST['subject'])." from ".$_POST['name']." | contact form feedback";

$mail->MsgHTML($msg);

$mail->Send();

unset($_SESSION['post']);	/* unsetting */

/* the form was successfully sent */
if($_POST['ajax'])
{
	echo '1';
}
else
{
	$_SESSION['sent']=1;

	if($_SERVER['HTTP_REFERER'])
		header('Location: '.$_SERVER['HTTP_REFERER']);

	exit;
}

/* some helpful functions */
function checkLen($str,$len=2)
{
	return isset($_POST[$str]) && mb_strlen(strip_tags($_POST[$str]),"utf-8") > $len;
}

function checkEmail($str)
{
	return preg_match("/^[\.A-z0-9_\-\+]+[@][A-z0-9_\-]+([.][A-z0-9_\-]+)+[A-z]{1,4}$/", $str);
}

Обратите внимание как мы проверяем переменную $_POST[‘ajax’] на предмет установки и правильной работы. Как вы наверное помните, мы устанавливаем ее ранее, в файле script.js, для определения используется ли аякс для извлечения данных.

Две переменные массива $_SESSION errStr и post используются для совместного использования данных между формой и файлом submit.php, в случае выключенного JS. Здесь переменная post содержит отправленный нами массив $_POST и используется для заполнения полей формы, после того как пользователь перенаправляется обратно.

На этом работа над нашей формой обратной связи подошла к концу.

Пример рабочий, за исключением одного маленького недоразумения. Плагин formValidation, создан с целью проводить валидацию только английских символов, так что если вы попытаетесь написать имя по-русски, он выдаст ошибку о том, что нужно вводить только буквы. К счастью это решается путем замены регулярного выражения в файле jquery.validationEngine.js. Найдите строчки:

"onlyLetter":{
	"regex":"/^[a-zA-Z\ \']+$/",
}	

и замените на следующую:

"onlyLetter":{
	"regex":"/^[a-zA-Zа-яА-Я' ]+$/",
}	

После этого, скрипт перестанет игнорировать русские символы.

Скачать архив с примером.

В связи с бесконечными вопросами, специально, отдельно сообщаю: настройка адреса email на который производится отправка писем, находится в файле submit.php, в самом начале файла, выглядит вот так:

/* config start */

$emailAddress = '';

/* config end */

Перевод статьи «A Fancy AJAX Contact Form«, автор Martin