Advertisement
  1. Web Design
  2. HTML/CSS
  3. HTML

Создаем аккуратную контактную форму на HTML5

Scroll to top
Read Time: 47 min

() translation by (you can also view the original English article)

В этом уроке мы узнаем, как создать шикарную контактную форму с поддержкой HTML5 AJAX. Форма будет использовать некоторые из новых элементов и атрибутов ввода HTML5 и будет проверена с помощью встроенной в браузер проверки формы.

Мы будем использовать jQuery и Modernizr, чтобы помочь со старыми браузерами, и PHP на стороне сервера для проверки ввода.


Шаг 1: Начало работы

Для начала нам нужно настроить наш каталог и файлы. Для начала я настоятельно рекомендую HTML5 boilerplate. Это действительно хорошая отправная точка для любого проекта HTML5 и сэкономит вам много времени. Для этого урока я выбрал «BOILERPLATE CUSTOM».

HTML5 BoilerplateHTML5 BoilerplateHTML5 Boilerplate

Для получения дополнительной информации о HTML5 boilerplate ознакомьтесь с этим руководством на Nettuts+.

После загрузки и распаковки удалите все, кроме index.html и папок css и js. Я также добавил папку с именем img и файл PHP с именем process.php. Мы будем использовать папку img для хранения изображений для нашей формы и process.php для обработки всей серверной логики для контактной формы. Вот как выглядит моя структура каталогов:

Directory StructureDirectory StructureDirectory Structure

Это все, что нам нужно для начала! Шаблон HTML5 включает в себя потрясающий набор CSS с разумными настройками по умолчанию и включает все библиотеки JS (jQuery & Modernizr), которые мы собираемся использовать сегодня. Все наши JS-файлы и CSS-файлы были подключены в index файле. Теперь пришло время перейти к разметке.


Шаг 2: Форма

Откройте index.html и удалите все внутри элемента #container. Мы поместим нашу контактную форму внутри этого div:

1
<div id="contact-form" class="clearfix">
2
    <h1>Get In Touch!</h1>
3
    <h2>Fill out our super swanky HTML5 contact form below to get in touch with us! Please provide as much information as possible for us to help you with your enquiry :)</h2>
4
    <ul id="errors" class="">
5
        <li id="info">There were some problems with your form submission:</li>
6
    </ul>
7
    <p id="success">Thanks for your message! We will get back to you ASAP!</p>
8
    <form method="post" action="process.php">
9
        <label for="name">Name: <span class="required">*</span></label>
10
        <input type="text" id="name" name="name" value="" placeholder="John Doe" required="required" autofocus="autofocus" />
11
        
12
        <label for="email">Email Address: <span class="required">*</span></label>
13
        <input type="email" id="email" name="email" value="" placeholder="johndoe@example.com" required="required" />
14
        
15
        <label for="telephone">Telephone: </label>
16
        <input type="tel" id="telephone" name="telephone" value="" />
17
        
18
        <label for="enquiry">Enquiry: </label>
19
        <select id="enquiry" name="enquiry">
20
            <option value="general">General</option>
21
            <option value="sales">Sales</option>
22
            <option value="support">Support</option>
23
        </select>
24
        
25
        <label for="message">Message: <span class="required">*</span></label>
26
        <textarea id="message" name="message" placeholder="Your message must be greater than 20 charcters" required="required" data-minlength="20"></textarea>
27
        
28
        <span id="loading"></span>
29
        <input type="submit" value="Holla!" id="submit-button" />
30
        <p id="req-field-desc"><span class="required">*</span> indicates a required field</p>
31
    </form>
32
</div>

Это весь HTML-код, который нам понадобится для нашей формы. Давайте посмотрим на каждый отдельный раздел:

ul#errors и p#success будут контейнерами наших сообщений об ошибках и успехах. Мы будем скрывать их по умолчанию с помощью CSS и отображать их с помощью JavaScript или PHP после отправки формы. Для ввода имени наше единственное требование - чтобы оно было заполнено.

В HTML5 мы делаем это путем добавления атрибута 'required'. Это заставит браузер проверить, что в этом поле есть что-то, прежде чем разрешить отправку формы. Поле электронной почты аналогично - обязательное, но также мы хотим убедиться, что введенный текст это адрес электронной почты. Для этого мы указываем тип поля ввода - электронная почта, который является новым в HTML5. Хотя телефон не является обязательным полем, мы используем для этого HTML5 тип поля ввода - tel.

Запрос (Enquiry) - это стандартный элемент select, а сообщение - обычный textarea - здесь нет ничего нового. Для textarea мы установим обязательный атрибут, чтобы убедиться, что пользователь вводит некоторый текст.

В HTML5 есть новый атрибут для текстовых областей, называемый maxlength. Да, вы уже догадались, это позволяет нам установить максимальное количество символов, которое мы можем написать в текстовой области. По какой-то причине те, кто создал спецификацию HTML5, не думали, что нам понадобится атрибут minlength (как мы делаем сейчас), и для этого нет атрибута. Таким образом, в качестве временного атрибута minlength мы собираемся использовать еще один новый атрибут HTML5, называемый пользовательским атрибутом data. По сути, это любое имя атрибута с префиксом «data-». В нашем случае мы выбрали data-minlength. Это позволяет нам создавать собственные атрибуты.

Еще одна вещь, на которую стоит обратить внимание, - это то, что мы устанавливаем атрибут placeholder для всех элементов ввода (кроме телефона) и текстовой области. Это новый атрибут HTML5 для элемента input. Когда форма отображается в первый раз, на входе появляется текст-заполнитель, обычно другого цвета. Затем, когда вы фокусируете ввод, текст заполнителя исчезает. Если вы ушли из поля, не заполнив его, текст заполнителя будет добавлен обратно. Это довольно крутой эффект, и он может предоставить пользователю немного больше информации о том, что ему нужно сделать. Ранее это должно было быть сделано с помощью JavaScript.

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

Это все возможности HTML5, которые мы собираемся включить в нашу разметку. Для получения более подробной информации об этих новых атрибутах и входных данных ознакомьтесь с некоторыми из этих ссылок:


Шаг 3: Стилизация формы

Вот наша форма, выглядит не очень...

Unstyled FormUnstyled FormUnstyled Form

В данный момент она выглядит не слишком хорошо, и она не делает нашу блестящую новизну HTML5 видимой, поэтому давайте добавим немного CSS. Откройте файл style.css. Файл уже содержит некоторые стили по умолчанию, которые помогут нам сделать нашу форму кросс-браузерной. Прокрутите вниз и найдите комментарий, говорящий:

1
/*

2
    // ========================================== \\

3
   ||                                              ||

4
   ||               Your styles !                  ||

5
   ||                                              ||

6
    \\ ========================================== //

7
*/

Сразу после этого вставьте следующий CSS:

1
#contact-form {
2
    background-color:#F2F7F9;
3
    width:465px;
4
    padding:20px;
5
    margin: 50px auto;    
6
    border: 6px solid #8FB5C1;
7
    -moz-border-radius:15px;
8
    -webkit-border-radius:15px;
9
    border-radius:15px;
10
    position:relative;
11
}
12
13
#contact-form h1 {
14
    font-size:42px;
15
}
16
17
#contact-form h2 {
18
    margin-bottom:15px;
19
    font-style:italic;
20
    font-weight:normal;
21
}
22
23
#contact-form input, 
24
#contact-form select, 
25
#contact-form textarea, 
26
#contact-form label {
27
    font-size:15px;
28
    margin-bottom:2px;
29
}
30
31
#contact-form input, 
32
#contact-form select, 
33
#contact-form textarea {
34
    width:450px;
35
    border: 1px solid #CEE1E8;
36
    margin-bottom:20px;
37
    padding:4px;
38
}
39
40
#contact-form input:focus, 
41
#contact-form select:focus, 
42
#contact-form textarea:focus {
43
    border: 1px solid #AFCDD8;
44
    background-color: #EBF2F4;
45
}
46
47
#contact-form textarea {
48
    height:150px;
49
    resize: none;
50
}
51
52
#contact-form label {
53
    display:block;
54
}
55
56
#contact-form .required {
57
    font-weight:bold;
58
    color:#F00;    
59
}
60
61
#contact-form #submit-button {
62
    width: 100px;
63
    background-color:#333;
64
    color:#FFF;
65
    border:none;
66
    display:block;
67
    float:right;
68
    margin-bottom:0px;
69
    margin-right:6px;
70
    background-color:#8FB5C1;
71
    -moz-border-radius:8px;
72
}
73
74
#contact-form #submit-button:hover {
75
    background-color: #A6CFDD;
76
}
77
78
#contact-form #submit-button:active {
79
    position:relative;
80
    top:1px;
81
}
82
83
#contact-form #loading {
84
    width:32px;
85
    height:32px;
86
    background-image:url(../img/loading.gif);
87
    display:block;
88
    position:absolute;
89
    right:130px;
90
    bottom:16px;
91
    display:none;
92
}
93
94
#errors {
95
    border:solid 1px #E58E8E;
96
    padding:10px;
97
    margin:25px 0px;
98
    display:block;
99
    width:437px;
100
    -webkit-border-radius:8px;
101
    -moz-border-radius:8px;
102
    border-radius:8px;
103
    background:#FFE6E6 url(../img/cancel_48.png) no-repeat 405px center;
104
    display:none;
105
}
106
107
#errors li {
108
    padding:2px;
109
    list-style:none;    
110
}
111
112
#errors li:before {
113
    content: ' - ';    
114
}
115
116
#errors #info {
117
    font-weight:bold;
118
}
119
120
#errors #info:before {
121
    content: '';    
122
}
123
124
#success {
125
    border:solid 1px #83D186;
126
    padding:25px 10px;
127
    margin:25px 0px;
128
    display:block;
129
    width:437px;
130
    -webkit-border-radius:8px;
131
    -moz-border-radius:8px;
132
    border-radius:8px;
133
    background:#D3EDD3 url(../img/accepted_48.png) no-repeat 405px center;
134
    font-weight:bold;
135
    display:none;
136
}
137
138
#errors.visible, #success.visible {
139
    display:block;    
140
}
141
142
#req-field-desc {
143
    font-style:italic;
144
}
145
146
/* Remove box shadow firefox, chrome and opera put around required fields. It looks rubbish. */
147
input:required, textarea:required {
148
    -moz-box-shadow:none;
149
    -webkit-box-shadow:none;
150
    -o-box-shadow:none;
151
    box-shadow:none;
152
}
153
154
/* Normalize placeholder styles */
155
156
/* chrome, safari */
157
::-webkit-input-placeholder {
158
    color:#CCC;
159
    font-style:italic;
160
}
161
162
/* mozilla */
163
input:-moz-placeholder, textarea:-moz-placeholder {
164
    color:#CCC;
165
    font-style:italic;
166
}
167
168
/* ie (faux placeholder) */
169
input.placeholder-text, textarea.placeholder-text  { 
170
    color:#CCC;
171
    font-style:italic;
172
}

Если вы сохраните и перезагрузите компьютер, ваша страница должна выглядеть так:

Styled FormStyled FormStyled Form

Теперь форма выглядит лучше! CSS довольно стандартный, но я остановлюсь на нескольких вещах, которые не так очевидны:

1
#errors li:before {
2
    content: ' - ';    
3
}

Это ставит черту рядом с нашими сообщениями о проверке ошибок. Это в заменяет пункт в списке, я думаю, что это выглядит лучше.

1
#contact-form #submit-button:active {
2
    position:relative;
3
    top:1px;
4
}

Это даст нам хороший эффект при нажатии, когда кнопка отправки активна.

1
input:required, textarea:required {
2
    -moz-box-shadow:none;
3
    -webkit-box-shadow:none;
4
    -o-box-shadow:none;
5
    box-shadow:none;
6
}

Все браузеры (кроме IE) по умолчанию помещают красную тень вокруг обязательных элементов. На мой взгляд, это выглядит немного чрезмерным, поэтому я его убираю. Я уже указывал, что для указания, что поле необходимо заполнить, надо поставить красную звездочку на элементе label.

Stupid looking required inputsStupid looking required inputsStupid looking required inputs
1
/* chrome, safari */
2
::-webkit-input-placeholder {
3
    color:#CCC;
4
    font-style:italic;
5
}
6
7
/* mozilla */
8
input:-moz-placeholder, textarea:-moz-placeholder {
9
    color:#CCC;
10
    font-style:italic;
11
}
12
13
/* ie (faux placeholder) */
14
input.placeholder-text, textarea.placeholder-text  { 
15
    color:#CCC;
16
    font-style:italic;
17
}

Это нормализует внешний вид текста-заполнителя на полях ввода и текстовых областях. Здесь мы делаем его светло-серым и выделяем его курсивом. Это даст нам одинаковое отображение во всех браузерах, кроме Opera, которая не поддерживает стилизацию placeholder. IE просто не поддерживает атрибут placeholder. Полная остановка. Мы будем использовать JavaScript, чтобы обойти это. Вы можете узнать больше о стилизации HTML5-форм с помощью CSS (2.1 + 3) здесь.

Вы заметите в CSS, что есть несколько ссылок на изображения. Если у вас их нет, просто загрузите исходные файлы для этого урока и скопируйте их заново.

Мы закончили с разметкой, и она выглядит довольно мило. Мы собираемся создать запасной вариант PHP в случае, если браузер пользователя не поддерживает новые атрибуты ввода формы (IE) или если у пользователя отключен JavaScript. Мы собираемся написать немного JavaScript позже, чтобы реализовать функции, отсутствующие в браузере. Но если у пользователя нет замечательного нового браузера или включен JavaScript, нам все равно нужно проверить отправку формы. Мы сделаем это на стороне сервера с помощью PHP. Мы также собираемся использовать его, чтобы отправить нам по электронной почте результаты формы.


Шаг 4: Подготовка к проверке на стороне сервера

Давайте погружаться. Откройте файл process.php и вставьте следующее:

1
<?php
2
if( isset($_POST) ){
3
    
4
    //form validation vars

5
    $formok = true;
6
    $errors = array();
7
    
8
    //sumbission data

9
    $ipaddress = $_SERVER['REMOTE_ADDR'];
10
    $date = date('d/m/Y');
11
    $time = date('H:i:s');
12
    
13
    //form data

14
    $name = $_POST['name'];    
15
    $email = $_POST['email'];
16
    $telephone = $_POST['telephone'];
17
    $enquiry = $_POST['enquiry'];
18
    $message = $_POST['message'];
19
    
20
    //form validation to go here....

21
    
22
}

Мы говорим здесь: выполнять следующий код только тогда, когда метод запроса - это POST. По умолчанию, если форма отправляется в сценарий PHP, входные значения формы хранятся в суперглобальном массиве с именем $_POST. Если ничего не пришло, $_POST не будет массивом, оператор if будет равен false и наш код не будет запущен.

Как только мы установим, что это запрос POST, мы можем начать логику обработки формы. Первое, что нам нужно сделать, это установить две переменные:

  • $formok: логическое значение, которое мы можем проверить, чтобы убедиться, что форма валидная или мы не в конце скрипта.
  • $errors: массив, который мы будем использовать для хранения всех ошибок в форме, так как мы проверяем ее.

После этого мы устанавливаем некоторые общие данные для отправки формы:

  • $ipaddress: IP-адрес пользователя, он может быть полезен для внесения в черный список спама, перекрестных ссылок на аналитические данные и т. д.
  • $date: дата отправки формы. Мы используем функцию date для генерации даты в британском формате.
  • $time: время отправки формы. Мы используем функцию date, чтобы сгенерировать время.

Мы могли бы объединить дату и время, если бы хотели:

1
$datetime = date('d/m/Y H:i:s');

Мне нравится хранить их отдельно, чтобы я мог использовать их для других вещей, если это необходимо. Последний набор переменных, которые мы устанавливаем, - это значения отправленных полей формы. Мы обращаемся к массиву $_POST, передавая имя поля формы в качестве ключа для извлечения данных для каждой переменной.


Шаг 5: Проверка данных $_POST

Мы собираемся проверить каждую переменную в отдельности, чтобы убедиться, что их значение является корректным. Если это не так, мы установим для переменной $formok значение false и сохраним сообщение об ошибке в массиве $errors. Мы начнем с поля имени.

1
//validate name is not empty

2
if(empty($name)){
3
    $formok = false;
4
    $errors[] = "You have not entered a name";
5
}

Здесь мы просто проверяем, что $name на самом деле имеет значение. Если это не так, это означает, что пользователь не ввел имя. Мы используем функцию empty(), чтобы проверить это. Скобки [] после $errors является аналого для array_push (который используется для добавления элемента в конец массива). Далее мы проверим адрес электронной почты:

1
//validate email address is not empty

2
if(empty($email)){
3
    $formok = false;
4
    $errors[] = "You have not entered an email address";
5
//validate email address is valid

6
}elseif(!filter_var($email, FILTER_VALIDATE_EMAIL)){
7
    $formok = false;
8
    $errors[] = "You have not entered a valid email address";
9
}

Мы собираемся проверить, был ли введен действительный адрес электронной почты. Для этой задачи мы будем использовать функцию filter_var(). Наконец, нам нужно проверить сообщение.

1
//validate message is not empty

2
if(empty($message)){
3
    $formok = false;
4
    $errors[] = "You have not entered a message";
5
}
6
//validate message is greater than 20 charcters

7
elseif(strlen($message) < 20){
8
    $formok = false;
9
    $errors[] = "Your message must be greater than 20 characters";
10
}

Еще раз, мы собираемся проверить, было ли введено сообщение. Если что-то было введено, мы хотим убедиться, что оно больше 20 символов. Для этого мы будем использовать функцию strlen().

Поле телефона и поле запроса не являются обязательными полями, поэтому нет необходимости проверять их. Вы могли бы, если хотите, но для целей этого урока это не обязательно.


Шаг 6: Что делать дальше...

После того, как мы проверили результаты нашей формы, нам нужно решить, отправлять ли пользователю электронное письмо с результатами формы или нет. Мы отслеживали правильность формы, используя переменную $formok. Если оно все еще равно равно true, мы хотим отправить результаты формы, в противном случае мы не будем.

Это логика, которую мы будем использовать для отправки сообщения (вставьте это после того, как мы выполнили нашу проверку):

1
//send email if all is ok

2
if($formok){
3
    $headers = "From: info@example.com" . "\r\n";
4
    $headers .= 'Content-type: text/html; charset=iso-8859-1' . "\r\n";
5
    
6
    $emailbody = "<p>You have recieved a new message from the enquiries form on your website.</p>

7
                  <p><strong>Name: </strong> {$name} </p>

8
                  <p><strong>Email Address: </strong> {$email} </p>

9
                  <p><strong>Telephone: </strong> {$telephone} </p>

10
                  <p><strong>Enquiry: </strong> {$enquiry} </p>

11
                  <p><strong>Message: </strong> {$message} </p>

12
                  <p>This message was sent from the IP Address: {$ipaddress} on {$date} at {$time}</p>";
13
    
14
    mail("enquiries@example.com","New Enquiry",$emailbody,$headers);
15
    
16
}

Чтобы отправить сообщение, мы будем использовать функцию mail(). Нам нужно будет передать этой функции четыре параметра: to, subject, message и headers.

  • to: Это будет адрес электронной почты, на который вы хотите отправить детали формы.
  • subject: это будет тема письма.
  • message: это будет содержание письма. Мы храним это в переменной $emailbody. Это HTML-строка, содержащая результаты нашей формы. Там, где вы видите фигурные скобки с именами наших переменных, они будут заменены на значения переменных при запуске этого скрипта. Это называется заменой переменных. Подстановка такого рода работает только в том случае, если строка заключена в кавычки DOUBLE, а не SINGLE.
  • headers: Это используется для передачи дополнительной информации почтовому клиенту, чтобы он знал, как отсылать электронную почту. Мы храним наши заголовки в переменной $headers и предоставляем дополнительную информацию о том, от кого получено электронное письмо и какой тип содержимого оно содержит.

Примечание. Не забудьте изменить адрес электронной почты from в заголовках и адрес электронной почты to в функции mail.

Это должно привести к подобному письму:

Email ScreenshotEmail ScreenshotEmail Screenshot

Если вы работаете на сервере Windows, вам может потребоваться вставить эту строку кода (перед объявлением переменной $headers), чтобы заставить работать функцию mail:

1
ini_set("sendmail_from","info@example.com");

Независимо от того, была ли отправка формы пользователем действительной или нет, мы хотим вернуть их обратно в форму. Если форма была валидной и сообщение было отправлено, мы должны предоставить пользователю сообщение об успехе. Если она невалидна, мы хотим отобразить сообщения об ошибках, хранящиеся в массиве $errors, а также заполнить поля формы данными, которые были первоначально отправлены. Мы будем хранить некоторые переменные, которые мы использовали в этом сценарии, в массиве и отправлять их вместе с перенаправлением обратно в форму.

1
//what we need to return back to our form

2
$returndata = array(
3
    'posted_form_data' => array(
4
        'name' => $name,
5
        'email' => $email,
6
        'telephone' => $telephone,
7
        'enquiry' => $enquiry,
8
        'message' => $message
9
    ),
10
    'form_ok' => $formok,
11
    'errors' => $errors
12
);

Мы будем хранить наши данные в ассоциативном массиве. Этот массив имеет три элемента:

  • posts_form_data: это будет массив, содержащий данные формы, которые были отправлены в сценарий.
  • form_ok: здесь мы будем хранить переменную $formok, и эта переменная будет проверена на странице формы, чтобы показать пользователю соответствующее сообщение.
  • errors: мы будем хранить переменную $errors. Эта переменная будет использоваться, если переменная $formok равна false.

Последнее, что нам нужно сделать, - это перенаправить пользователя обратно на страницу формы вместе с нашим массивом $returndata. Как только мы будем перенаправлены обратно на страницу формы, мы потеряем нашу переменную $returndata; поэтому, чтобы сделать эти данные постоянными, мы временно сохраним их в сессии.

Еще одна вещь, которую мы должны иметь в виду, в конечном варианте, если в браузере пользователя включен JavaScript, мы хотим отправить форму через AJAX. Это будет означать, что мы хотим, чтобы наш AJAX-запрос был размещен в том же месте, что и отправка формы, когда JavaScript отключен. Поскольку форма уже была бы проверена на стороне клиента, она пройдет через всю проверку на стороне сервера, и подробности будут отправлены нам по электронной почте. Если форма невалидна, она никогда не будет отправлена (так как проверка браузера/JavaScript предотвратит это). Это означает, что с помощью запроса AJAX у нас нет причин для перенаправления или установки каких-либо переменных сессии. В заключительной части этого скрипта мы проверим, был ли текущий запрос к process.php запросом AJAX или нет, и, если это так, установите переменные сессии и перенаправьте.

1
//if this is not an ajax request

2
if(empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) !== 'xmlhttprequest'){
3
    
4
    //set session variables

5
    session_start();
6
    $_SESSION['cf_returndata'] = $returndata;
7
    
8
    //redirect back to form

9
    header('location: ' . $_SERVER['HTTP_REFERER']);
10
11
}

Чтобы проверить, был ли это AJAX-запрос, мы ищем переменную $_SERVER['HTTP_X_REQUESTED_WITH']. Как и суперглобальный массив $_POST, есть еще один, называемый $_SERVER. Этот массив содержит информацию о сервере и среде выполнения. Обратитесь сюда для получения более подробной информации.

Затем мы вызываем session_start(), чтобы дать нам доступ к сессии, и устанавливаем переменную $_SESSION['cf_returndata'] для зеркального отображения $returndata. На странице формы мы теперь сможем получить доступ к этой переменной.

Чтобы перенаправить обратно на форму, мы используем функцию header(). Мы говорим ему перенаправить нас на последнюю страницу, с которой мы пришли, используя переменную: $_SERVER['HTTP_REFERER'].

В целом вы должны иметь в итоге это:

1
<?php
2
if( isset($_POST) ){
3
    
4
    //form validation vars

5
    $formok = true;
6
    $errors = array();
7
    
8
    //submission data

9
    $ipaddress = $_SERVER['REMOTE_ADDR'];
10
    $date = date('d/m/Y');
11
    $time = date('H:i:s');
12
    
13
    //form data

14
    $name = $_POST['name'];    
15
    $email = $_POST['email'];
16
    $telephone = $_POST['telephone'];
17
    $enquiry = $_POST['enquiry'];
18
    $message = $_POST['message'];
19
    
20
    //validate form data

21
    
22
    //validate name is not empty

23
    if(empty($name)){
24
        $formok = false;
25
        $errors[] = "You have not entered a name";
26
    }
27
    
28
    //validate email address is not empty

29
    if(empty($email)){
30
        $formok = false;
31
        $errors[] = "You have not entered an email address";
32
    //validate email address is valid

33
    }elseif(!filter_var($email, FILTER_VALIDATE_EMAIL)){
34
        $formok = false;
35
        $errors[] = "You have not entered a valid email address";
36
    }
37
    
38
    //validate message is not empty

39
    if(empty($message)){
40
        $formok = false;
41
        $errors[] = "You have not entered a message";
42
    }
43
    //validate message is greater than 20 characters

44
    elseif(strlen($message) < 20){
45
        $formok = false;
46
        $errors[] = "Your message must be greater than 20 characters";
47
    }
48
    
49
    //send email if all is ok

50
    if($formok){
51
        $headers = "From: info@example.com" . "\r\n";
52
        $headers .= 'Content-type: text/html; charset=iso-8859-1' . "\r\n";
53
        
54
        $emailbody = "<p>You have received a new message from the enquiries form on your website.</p>

55
                      <p><strong>Name: </strong> {$name} </p>

56
                      <p><strong>Email Address: </strong> {$email} </p>

57
                      <p><strong>Telephone: </strong> {$telephone} </p>

58
                      <p><strong>Enquiry: </strong> {$enquiry} </p>

59
                      <p><strong>Message: </strong> {$message} </p>

60
                      <p>This message was sent from the IP Address: {$ipaddress} on {$date} at {$time}</p>";
61
        
62
        mail("enquiries@example.com","New Enquiry",$emailbody,$headers);
63
        
64
    }
65
    
66
    //what we need to return back to our form

67
    $returndata = array(
68
        'posted_form_data' => array(
69
            'name' => $name,
70
            'email' => $email,
71
            'telephone' => $telephone,
72
            'enquiry' => $enquiry,
73
            'message' => $message
74
        ),
75
        'form_ok' => $formok,
76
        'errors' => $errors
77
    );
78
        
79
    
80
    //if this is not an ajax request

81
    if(empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) !== 'xmlhttprequest'){
82
        //set session variables

83
        session_start();
84
        $_SESSION['cf_returndata'] = $returndata;
85
        
86
        //redirect back to form

87
        header('location: ' . $_SERVER['HTTP_REFERER']);
88
    }
89
}

Все, что нужно для обработки отправки нашей формы, сделано и уместилось менее чем в 90 строках PHP! Все, что нам нужно сделать сейчас, это обновить пользователя и предоставить либо сообщение об успехе, либо сообщение об ошибке. Вы можете сохранить process.php сейчас.


Шаг 7: Обновим интерфейс

Теперь, когда мы обработали данные формы и вернулись на страницу, нам нужно сообщить пользователю о том, что произошло. Это означает доступ к переменной сессии, которую мы установили в process.php, и выяснение того, какой ответ дать. Поскольку эта страница теперь должна использовать PHP, нам нужно изменить расширение файла index.html на .php (index.html = index.php). Не волнуйтесь, это не должно нарушать того, что мы уже сделали.

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

1
<?php session_start() ?>

Запуск сессии до того, как какой-либо контент будет отправлен в браузер, должен предотвратить возникновение ошибок типа «cannot send session cookie - headers already sent by...». Ниже элемента H2 формы добавляем в этот фрагмент PHP:

1
<?php
2
//init variables

3
$cf = array();
4
$sr = false;
5
6
if(isset($_SESSION['cf_returndata'])){
7
    $cf = $_SESSION['cf_returndata'];
8
    $sr = true;
9
}
10
?>

Мы устанавливаем две переменные в значения по умолчанию. Подробнее об этом позже ... Затем мы проверяем, установлена ли $_SESSION['cf_returndata']. Затем мы устанавливаем $cf (сокращение от контактной формы) равным нашей переменной сессии. Это просто для того, чтобы нам не печатать $_SESSION... каждый раз, когда мы хотим получить доступ к этим данным. Последняя переменная $sr (если не считать ответа сервера) имеет значение true. Это переменная, которую мы будем проверять, чтобы увидеть, отсылали ли мы ранее нашу форму. Следующее, что мы хотим сделать, это отобразить сообщение об ошибке или успехе в верхней части формы. Заменим это:

1
<ul id="errors" class="">
2
    <li id="info">There were some problems with your form submission:</li>
3
</ul>
4
<p id="success">Thanks for your message! We will get back to you ASAP!</p>

На это:

1
<ul id="errors" class="<?php echo ($sr && !$cf['form_ok']) ? 'visible' : ''; ?>">
2
    <li id="info">There were some problems with your form submission:</li>
3
    <?php 
4
    if(isset($cf['errors']) && count($cf['errors']) > 0) :
5
        foreach($cf['errors'] as $error) :
6
    ?>
7
    <li><?php echo $error ?></li>
8
    <?php
9
        endforeach;
10
    endif;
11
    ?>
12
</ul>
13
<p id="success" class="<?php echo ($sr && $cf['form_ok']) ? 'visible' : ''; ?>">Thanks for your message! We will get back to you ASAP!</p>

По умолчанию сообщения не отображаются вообще, потому что в CSS мы установили 'display: none'. Внутри атрибута класса сообщений мы используем PHP, чтобы добавить к ним класс 'visible', если они должны быть показаны. Этот класс устанавливает 'display' в 'block'.

1
<?php echo ($sr && !$cf['form_ok']) ? 'visible' : ''; ?>

Мы используем тернарный оператор здесь, чтобы проверить, что ...

  • a) ответ сервера равен true и
  • b) что форма была невалидная
  • .

По сути, если мы отправили форму, $sr будет равно true, а если форма была невалидной, $cf['form_ok'] будет равно false. Таким образом, класс visible будет выведен, но PHP и сообщение будут показаны, и наоборот для сообщения об успехе. Внутри скобок мы проверяем значения двух переменных. Мы проверяем, что $sr равно true, а (&&) $cf['fomr_ok'] равно false. Мы используем такое выражение для проверки этих значений. Вы также можете написать это так, если хотите:

1
<?php echo ($sr === true && $cf['form_ok'] === false) ? 'visible' : ''; ?>

После того, как мы решили, какое сообщение показывать, нам нужно заполнить контейнер соответствующими данными. Сообщение об успехе не меняется, поэтому мы можем оставить все как есть. Сообщение об ошибке нужно будет заполнить ошибками проверки. Чтобы записать их, мы просто перебираем наш массив ошибок, хранящийся в сессии, и заполняем элемент li внутри ul:

1
<ul id="errors" class="<?php echo ($sr && !$cf['form_ok']) ? 'visible' : ''; ?>">
2
    <li id="info">There were some problems with your form submission:</li>
3
    <?php 
4
    if(isset($cf['errors']) && count($cf['errors']) > 0) :
5
        foreach($cf['errors'] as $error) :
6
    ?>
7
    <li><?php echo $error ?></li>
8
    <?php
9
        endforeach;
10
    endif;
11
    ?>
12
</ul>

Сначала мы проверяем, что у нас есть массив ошибок в $cf и что он содержит хотя бы одну ошибку. Операторы if и foreach могут немного отличаться от того, что вы видели раньше. Это называется Альтернативным Синтаксисом. Мы использовали альтернативный синтаксис здесь просто для того, чтобы сделать его немного более читаемым при смешивании с HTML. Вы можете использовать обычный синтаксис, хотя, все зависит от предпочтений.

Это все, что нам нужно, чтобы показать пользователю ответ на отправку формы. Чтобы проверить это, отключите JavaScript и отправьте форму. Помните, что браузер будет проверять форму, так как мы используем новые элементы HTML5. Чтобы быть уверенным, что мой PHP работает, я тестирую в IE8. Да, верно, IE иногда пригодится ...

Если вы отправите невалидную форму, вы должны получить это:

Error MessageError MessageError Message

И если вы правильно заполните форму, вы должны получить:

Success MessageSuccess MessageSuccess Message

Вы также должны были получить электронное письмо, согласно коду, который мы написали ранее (если вы правильно заполнили форму). Теперь, когда форма работает, последнее, что нам нужно сделать, это снова заполнить поля формы данными пользователя, если отправка была неверной. Поменяйте HTML внутри тегов формы для этого:

1
<label for="name">Name: <span class="required">*</span></label>
2
<input type="text" id="name" name="name" value="<?php echo ($sr && !$cf['form_ok']) ? $cf['posted_form_data']['name'] : '' ?>" placeholder="John Doe" required="required" autofocus="autofocus" />
3
4
<label for="email">Email Address: <span class="required">*</span></label>
5
<input type="email" id="email" name="email" value="<?php echo ($sr && !$cf['form_ok']) ? $cf['posted_form_data']['email'] : '' ?>" placeholder="johndoe@example.com" required="required" />
6
7
<label for="telephone">Telephone: </label>
8
<input type="tel" id="telephone" name="telephone" value="<?php echo ($sr && !$cf['form_ok']) ? $cf['posted_form_data']['telephone'] : '' ?>" />
9
10
<label for="enquiry">Enquiry: </label>
11
<select id="enquiry" name="enquiry">
12
    <option value="General" <?php echo ($sr && !$cf['form_ok'] && $cf['posted_form_data']['enquiry'] == 'General') ? "selected='selected'" : '' ?>>General</option>
13
    <option value="Sales" <?php echo ($sr && !$cf['form_ok'] && $cf['posted_form_data']['enquiry'] == 'Sales') ? "selected='selected'" : '' ?>>Sales</option>
14
    <option value="Support" <?php echo ($sr && !$cf['form_ok'] && $cf['posted_form_data']['enquiry'] == 'Support') ? "selected='selected'" : '' ?>>Support</option>
15
</select>
16
17
<label for="message">Message: <span class="required">*</span></label>
18
<textarea id="message" name="message" placeholder="Your message must be greater than 20 charcters" required="required" data-minlength="20"><?php echo ($sr && !$cf['form_ok']) ? $cf['posted_form_data']['message'] : '' ?></textarea>
19
20
<span id="loading"></span>
21
<input type="submit" value="Holla!" id="submit-button" />
22
<p id="req-field-desc"><span class="required">*</span> indicates a required field</p>

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

1
<?php echo ($sr && !$cf['form_ok']) ? $cf['posted_form_data']['name'] : '' ?>

Как и в случае с сообщениями об успехе и ошибках, мы проверяем, равно ли значение $sr true, а значение $cf['form_ok'] равно false, и если так и есть, мы записываем сохраненное значение в сессию для этого поля формы. Это делается с помощью тернарного оператора.

В случае элемента select мы делаем то же самое, за исключением того, что вместо записи сохраненного значения нам нужно проверить значение каждого элемента option, чтобы увидеть, соответствует ли оно значению, сохраненному в сессии. Если оно совпадает, мы выводим атрибут selected для этой опции.

Наконец, последнее, что мы собираемся сделать, это unset эту переменную сессии после того, как мы получили от нее наши данные. Вы не обязаны делать это, все сводится к предпочтениям. Если удалить ее сейчас, когда страница обновляется с помощью кнопки обновления (не отправка формы), сообщение об ошибке/успехе не будет отображаться. Если вы не удалили ее, пользователь может заполнить контактную форму, поискать информацию в Интернете, вернуться к форме, и сообщение об ошибке/успехе все равно будет отображаться. Мне это не нравится, поэтому я собираюсь предотвратить это, поместив эту строку PHP сразу после закрывающих тегов формы:

1
<?php unset($_SESSION['cf_returndata']); ?>

Если вы отправляете невалидную форму, вы должны заметить, что введенные значения формы сохранены, и если вы обновите страницу, сообщение и данные должны быть удалены. Вот и все, что касается PHP! В итоге ваша форма должна выглядеть так:

1
<div id="contact-form" class="clearfix">
2
    <h1>Get In Touch!</h1>
3
    <h2>Fill out our super swanky HTML5 contact form below to get in touch with us! Please provide as much information as possible for us to help you with your enquiry :)</h2>
4
    <?php
5
    //init variables

6
    $cf = array();
7
    $sr = false;
8
    
9
    if(isset($_SESSION['cf_returndata'])){
10
        $cf = $_SESSION['cf_returndata'];
11
        $sr = true;
12
    }
13
    <ul id="errors" class="<?php echo ($sr && !$cf['form_ok']) ? 'visible' : ''; ?>">
14
        <li id="info">There were some problems with your form submission:</li>
15
        <?php 
16
        if(isset($cf['errors']) && count($cf['errors']) > 0) :
17
            foreach($cf['errors'] as $error) :
18
        ?>
19
        <li><?php echo $error ?></li>
20
        <?php
21
            endforeach;
22
        endif;
23
        ?>
24
    </ul>
25
    <form method="post" action="process.php">
26
        <label for="name">Name: <span class="required">*</span></label>
27
        <input type="text" id="name" name="name" value="<?php echo ($sr && !$cf['form_ok']) ? $cf['posted_form_data']['name'] : '' ?>" placeholder="John Doe" required autofocus />
28
        
29
        <label for="email">Email Address: <span class="required">*</span></label>
30
        <input type="email" id="email" name="email" value="<?php echo ($sr && !$cf['form_ok']) ? $cf['posted_form_data']['email'] : '' ?>" placeholder="johndoe@example.com" required />
31
        
32
        <label for="telephone">Telephone: </label>
33
        <input type="tel" id="telephone" name="telephone" value="<?php echo ($sr && !$cf['form_ok']) ? $cf['posted_form_data']['telephone'] : '' ?>" />
34
        
35
        <label for="enquiry">Enquiry: </label>
36
        <select id="enquiry" name="enquiry">
37
            <option value="General" <?php echo ($sr && !$cf['form_ok'] && $cf['posted_form_data']['enquiry'] == 'General') ? "selected='selected'" : '' ?>>General</option>
38
            <option value="Sales" <?php echo ($sr && !$cf['form_ok'] && $cf['posted_form_data']['enquiry'] == 'Sales') ? "selected='selected'" : '' ?>>Sales</option>
39
            <option value="Support" <?php echo ($sr && !$cf['form_ok'] && $cf['posted_form_data']['enquiry'] == 'Support') ? "selected='selected'" : '' ?>>Support</option>
40
        </select>
41
        
42
        <label for="message">Message: <span class="required">*</span></label>
43
        <textarea id="message" name="message" placeholder="Your message must be greater than 20 charcters" required data-minlength="20"><?php echo ($sr && !$cf['form_ok']) ? $cf['posted_form_data']['message'] : '' ?></textarea>
44
        
45
        <span id="loading"></span>
46
        <input type="submit" value="Holla!" id="submit-button" />
47
        <p id="req-field-desc"><span class="required">*</span> indicates a required field</p>
48
    </form>
49
    <?php unset($_SESSION['cf_returndata']); ?>
50
</div>

Не забывайте session_start() в верхней части страницы! Теперь у нас есть полнофункциональная контактная форма.

Данные проверены, и, в случае успеха, мы отправим результаты по электронной почте. Далее, мы обновляем пользовательский интерфейс с результатами для каждого представления. Новые браузеры проверят форму перед ее отправкой, используя новые типы полей ввода и атрибуты HTML5, которые мы использовали.

Это все прекрасно и здорово, но мы можем сделать еще один шаг вперед. Мы можем использовать JavaScript, чтобы реализовать функции, которых нет в браузере (встроенная проверка, поддержка атрибутов HTML5 и т. д.). Мы даже можем использовать JavaScript для отображения наших сообщений об ошибке/успехе и отправить форму с использованием AJAX.

Но зачем это делать, когда форма уже работает? Ну, это просто. Мы хотим обеспечить как можно большую согласованность во всех браузерах, даже если это действительно ненадежный браузер. Кроме того, если мы заставим браузер клиента делать всю работу по проверке, он сэкономит ресурсы нашего сервера, поскольку мы не отправляем данные, когда форма невалидная. Эти вещи - очень полезны и круты, и на самом деле это не так сложно сделать.


Шаг 8: Что такое полифилл?

«Полифилл или полифиллер - это фрагмент кода, который предоставляет технологию, которую вы, разработчик, ожидаете от браузера».

В нашем случае мы ожидаем, что браузер будет поддерживать новые типы полей ввода и атрибуты HTML5, которые мы использовали. Firefox, Chrome, Opera и Safari имеют довольно хорошую встроенную поддержку для них. IE6 - 9 вообще не поддерживает их. Типично. Честно говоря, это довольно шокирующее, что IE9 не имеет поддержки этих вещей, он был выпущен в начале этого года. В любом случае, откладывая IE в сторону (я мог бы продолжать бесконечно), первые две вещи, которые мы собираемся дополнить, - это autofocus и атрибут placeholder.

Мы будем использовать jQuery, чтобы помочь нам с нашим JavaScript. Мы будем использовать его в первую очередь для обработки нашего AJAX-запроса, анимации и обхода и манипуляции с DOM. Вы можете обойтись без его использования, но вам придется написать значительное количество кода. Его размер не слишком велик, поэтому я могу смириться с загрузкой файла такого размера. Я, вероятно, как и вы, предпочел бы написать меньше кода.

Мы также будем использовать библиотеку JavaScript Modernizr, чтобы помочь нам с обнаружением функций. Это уже включено как часть нашего шаблона HTML5, поэтому нам не нужно ничего делать, чтобы запустить Modernizr!

Перейдите в каталог js и откройте скрипт script.js. Нам не нужно беспокоиться о подключении этого файла, jQuery или Modernizr, к index.php, так как он уже был предоставлен нам для HTML5 boilerplate, который мы использовали. Удалите все в этом файле и вставьте следующее:

1
$(function(){
2
3
    //set global variables and cache DOM elements for reuse later
4
    var form = $('#contact-form').find('form'),
5
        formElements = form.find('input[type!="submit"],textarea'),
6
        formSubmitButton = form.find('[type="submit"]'),
7
        errorNotice = $('#errors'),
8
        successNotice = $('#success'),
9
        loading = $('#loading'),
10
        errorMessages = {
11
            required: ' is a required field',
12
            email: 'You have not entered a valid email address for the field: ',
13
            minlength: ' must be greater than '
14
        }
15
16
    //feature detection + polyfills
17
    formElements.each(function(){
18
    
19
        //do feature detection + polyfills here
20
        
21
    });
22
});

Весь наш код будет внутри блока $(function(){ }). Это будет означать, что наш код будет выполнен, как только страница загрузится. Также любые переменные или функции, которые мы объявляем внутри этого блока, не будут мешать любому другому коду снаружи. Затем мы кешируем некоторые элементы DOM, так как к ним мы будем обращаться довольно часто. Кэшировать их более эффективно, чем запрашивать их каждый раз, когда вы хотите их использовать. Вот объяснение того, что является каждой переменной:

  • form: Элемент контактной формы.
  • formElements: Все элементы ввода и текстовые области в форме, кроме кнопки отправки. Это будет просто массив элементов.
  • formSubmitButton: Кнопка отправки формы.
  • errorNotice: Уведомление об ошибке - неупорядоченный элемент списка.
  • successNotice: Сообщение об успехе - элемент абзаца.
  • loading: Элемент прогресса загрузки. Он отобразит 'загрузочный' GIF, когда форма будет отправлена после проверки.
  • errorMessages: Это объект, содержащий некоторый текст для наших сообщений об ошибках. Они используются более одного раза, поэтому мы создаем их здесь. Вы заметите, что некоторые сообщения не читаются правильно. Мы будем динамически добавлять их позже, когда перейдем к проверке формы.

После этого мы используем функцию jQuery, которая называется each(), для перебора массива formElements. Пока мы выполняем итерации по элементам формы, мы хотим выполнить обнаружение объектов для атрибута placeholder, и если у элемента есть этот атрибут, но он не поддерживается браузером, применим наш polyfill. Вот polyfill для атрибута placeholder:

1
//if HTML5 input placeholder attribute is not supported
2
if(!Modernizr.input.placeholder){
3
    var placeholderText = this.getAttribute('placeholder');
4
    if(placeholderText){
5
        $(this)
6
            .addClass('placeholder-text')
7
            .val(placeholderText)
8
            .bind('focus',function(){
9
                if(this.value == placeholderText){
10
                    $(this)
11
                        .val('')
12
                        .removeClass('placeholder-text');
13
                }
14
            })
15
            .bind('blur',function(){
16
                if(this.value == ''){
17
                    $(this)
18
                        .val(placeholderText)
19
                        .addClass('placeholder-text');
20
                }
21
            });
22
    }
23
}

Здесь мы используем Modernizr, чтобы определить, есть ли у нас поддержка атрибута placeholder. Modernizer - это объект, input - это свойство этого объекта, а placeholder - это свойство поля ввода (отсюда и все точки). Это значение будет либо true, либо false. Мы проверяем, является ли оно false (браузер не поддерживает атрибут placeholder); если это так, мы реализуем наш полифилл. Первое, что мы делаем, это объявляем переменную, которая будет содержать текст placeholder-а, назначенный элементу. Даже если браузер не поддерживает атрибут placeholder, мы все равно можем получить доступ к этому атрибуту. Для этого мы используем функцию с именем getAttribute(). Ключевое слово 'this' относится к текущему элементу DOM, который мы повторяем в цикле.

Получив текст placeholder-а, мы можем проверить, не является ли он пустым. Это значит, что мы применяем наш полифилл только к тем полям ввода, которые имеют атрибут placeholder. Затем мы объединяем несколько действительно полезных функций jQuery для создания нашего полифилла. Вот разъяснение того, что мы делаем:

  1. Мы заключаем ключевое слово 'this' в функцию jQuery ($()), поэтому имеем доступ к некоторым удобным функциям DOM jQuery.
  2. Мы добавляем класс 'placeholder-text' к элементу. Это сделает текст placeholder-ов элементов, которые мы собираемся починить, похожим на остальные браузеры. Мы уже установили правило для этого в CSS.
  3. Мы устанавливаем значение по умолчанию в значение атрибута placeholder для поля ввода. Это покажет текст placeholder в поле ввода, когда страница загружена.
  4. Мы привязываем событие фокуса, которое проверит, совпадает ли текст атрибута placeholder со значением входных данных. Если это так, тогда для значения ввода ничего не задано, что очищает ввод, и мы удаляем класс 'placeholder-text', чтобы текст являлся текстом ввода по умолчанию.
  5. Мы привязываем событие blur, которое проверит, равно ли значение ввода пустоте. Если это так, мы заполняем ввод текстом-placeholder и повторно применяем 'placeholder-text'.

Это заставит любой браузер, который не поддерживает атрибут placeholder, действовать так, как если бы он поддерживал этот атрибут. Смотрите изображение ниже из IE8:

Place Holder StatesPlace Holder StatesPlace Holder States

Далее мы добавим атрибут autofocus. Это очень просто:

1
//if HTML5 input autofocus attribute is not supported
2
if(!Modernizr.input.autofocus){
3
    if(this.getAttribute('autofocus')) this.focus();
4
}

Мы используем Modernizer, чтобы определить, поддерживается ли атрибут autofocus. Если нет, то мы проверяем, имеет ли этот элемент установленный атрибут autofocus, и если это так, мы фокусируем его. Просто. В любом браузере, который не поддерживает этот атрибут, это будет исправлено.

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


Шаг 9: Проверка формы, стили Polyfill

1
//to ensure compatibility with HTML5 forms, we have to validate the form on submit button click event rather than form submit event. 
2
//An invalid html5 form element will not trigger a form submit.
3
formSubmitButton.bind('click',function(){
4
    var formok = true,
5
        errors = [];
6
        
7
    formElements.each(function(){
8
           
9
        //validate form elements here
10
           
11
    });
12
    
13
    //if form is not valid
14
    if(!formok){
15
        
16
        //show error message here
17
        
18
    }
19
    //if form is valid
20
    else {
21
        
22
        //ajax request + show success message here
23
        
24
    }
25
    
26
    return false; //this stops submission off the form and also stops browsers showing default error message
27
});

Мы связываем событие click с кнопкой отправки формы (хранится в переменной formSubmitButton). Когда это событие сработает, мы проверим форму. Обычно в JavaScript мы делаем это для события submit формы, но поскольку новые браузеры используют свои собственные встроенные средства проверки, событие submit формы никогда не запускается. Браузер будет отображать свои собственные сообщения об ошибках, но они крайне противоречивы во всех браузерах, и в настоящее время нет способа их стилизовать. Отображение нашего собственного сообщения об ошибке обеспечит согласованность, а также покажет для браузеров, которые не поддерживают новые методы проверки. Чтобы браузеры не отображали свои сообщения об ошибках по умолчанию, мы возвращаем false в конце этой функции. Вот объяснение того, для чего нужны переменные, установленные вверху:

  • formok: Это будет отслеживать правильность формы.
  • errors: Это массив, который будет содержать сообщения об ошибках.

Это похоже на валидацию PHP, которую мы написали ранее!

Мы начнем внутри цикла, где будем проверять элементы формы. Внутри этого цикла мы хотим начать с объявления некоторых полезных переменных, которые мы будем использовать при проверке.

1
var name = this.name,
2
    nameUC = name.ucfirst(),
3
    value = this.value,
4
    placeholderText = this.getAttribute('placeholder'),
5
    type = this.getAttribute('type'), //get type old school way
6
    isRequired = this.getAttribute('required'),
7
    minLength = this.getAttribute('data-minlength');
  • name: Имя текущего элемента.
  • nameUC: Имя текущего элемента с заглавной буквой. ucfirst() - это пользовательский метод строкового объекта, который мы напишем позже.
  • value: Значение текущего элемента.
  • placeholderText: Текст placeholder-а текущего элемента.
  • type: Тип текущего элемента.
  • isRequired: Установлен ли у текущего элемента обязательный атрибут или нет.
  • minLength: Значение data-minlength текущего элемента (если применимо).

Теперь, когда у нас установлены переменные, мы можем начать с проверки. Для элементов, которые используют типы полей ввода и атрибуты HTML5, мы можем использовать новый API JavaScript для проверки их достоверности.

В HTML5 элементы формы имеют новое свойство, называемое validity. Здесь хранятся все данные проверки для этого элемента. В Firebug это выглядит так:

Firebug ScreenshotFirebug ScreenshotFirebug Screenshot

Как вы можете видеть, у этого объекта есть множество свойств, которые дают нам немного больше информации о том, в чем проблема. Значения свойств являются либо false либо false. На этом скриншоте я попытался отправить форму без имени и зарегистрировал свойство validity для ввода имени в консоли (console.log(this.validity) ). Это показывает, что значение отсутствовало (valueMissing = true).

Наш код для проверки элементов HTML5:

1
//if HTML5 formfields are supported            
2
if( (this.validity) && !this.validity.valid ){
3
    formok = false;
4
    
5
    //if there is a value missing
6
    if(this.validity.valueMissing){
7
        errors.push(nameUC + errorMessages.required);    
8
    }
9
    //if this is an email input and it is not valid
10
    else if(this.validity.typeMismatch && type == 'email'){
11
        errors.push(errorMessages.email + nameUC);
12
    }
13
    
14
    this.focus(); //safari does not focus element an invalid element
15
    return false;
16
}

Мы проверяем, имеет ли этот элемент формы свойство validity, и, если оно есть, мы проверяем свойство valid объекта validity, чтобы убедиться, что это поле в порядке. Если он невалиден (я использую сокращение,!, для проверки на false), мы устанавливаем formok в false и выполняем некоторые тесты, чтобы увидеть, в чем проблема.

Если значение отсутствует (вызвано обязательными полями), мы добавляем сообщение об ошибке в массив ошибок, который мы установили ранее. Для этого мы используем метод push() объекта массива. Сообщение об ошибке будет состоять из имени элемента (первая буква в верхнем регистре), соединенного с требуемым сообщением об ошибке, которое мы установили ранее в нашем скрипте.

Если значение этого поля формы не пропущено, мы хотим определить, были ли введены правильные данные. Единственное поле ввода в нашей форме, который нуждается в проверке, это поле электронной почты. Имея это в виду, в elseif части нашего кода мы проверяем, равно ли свойство typeMismatch объекта validity true и является ли тип этого поля ввода действительно электронной почтой. Если это так, мы добавляем сообщение об ошибке электронной почты в наш массив ошибок.

Когда браузер проверяет поле и определяет его невалидным, оно автоматически фокусируется. Safari не поддерживает это, поэтому для согласованности мы вручную фокусируем ввод. Затем мы возвращаем false в конце проверки правильности ввода HTML5, чтобы выйти из цикла, поскольку мы знаем, что у нас есть недопустимый элемент (нам не нужно тратить время на проверку остальных элементов в форме).

Это будет проверять наши входные данные HTML5, но теперь нам нужно ориентироваться на браузеры, которые не поддерживают API проверки форм JavaScript. Если API проверки формы JavaScript не поддерживается браузером, приведенный выше код никогда не будет выполнен и будет пропущен.

Первое, что мы проверим, является ли поле обязательным. Наш полифилл для этого будет выглядеть так:

1
//if this is a required element
2
if(isRequired){    
3
    //if HTML5 input required attribute is not supported
4
    if(!Modernizr.input.required){
5
        if(value == placeholderText){
6
            this.focus();
7
            formok = false;
8
            errors.push(nameUC + errorMessages.required);
9
            return false;
10
        }
11
    }
12
}

Во-первых, мы проверяем, является ли это поле обязательным (определяется атрибутом required). Затем мы используем Modernizr, чтобы проверить, поддерживается ли атрибут required браузером. Если нет, нам нужно вручную проверить значение элемента и сравнить его с атрибутом placeholder. Если они одинаковые, то, очевидно, это поле формы не заполнено, поэтому мы делаем четыре вещи:

  1. Мы фокусируем ввод (как это делает браузер при использовании его собственной проверки)
  2. Мы устанавливаем переменную formok в false, так как форма невалидна.
  3. Мы добавляем сообщение об ошибке в наш массив ошибок.
  4. Мы возвращаем false, этим выходим из цикла и сразу переходим к следующему фрагменту кода вне цикла.

Затем мы собираемся проверить, является ли это поле ввода полем электронной почты, и, если это так, был ли введен действительный адрес электронной почты.

1
//if HTML5 input email input is not supported
2
if(type == 'email'){     
3
    if(!Modernizr.inputtypes.email){ 
4
        var emailRegEx = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/; 
5
        if( !emailRegEx.test(value) ){    
6
            this.focus();
7
            formok = false;
8
            errors.push(errorMessages.email + nameUC);
9
            return false;
10
        }
11
    }
12
}

Это делается почти так же, как и раньше. Мы видим, действительно ли это поле электронной почты, а затем используем Modernizr, чтобы проверить, поддерживается ли поле электронной почты. Если это не так, мы пишем наш код, который проверяет, является ли она действительной или нет. Для этого полифилла мы используем регулярные выражения, чтобы проверить, является ли электронная почта действительной или нет. Мы создаем регулярное выражение в переменной emailRegEx, а затем используем метод test() объекта регулярного выражения, чтобы проверить, соответствует ли значение поля ввода регулярному выражению.

Вы можете узнать больше об использовании регулярных выражений JavaScript здесь.

Если адрес электронной почты недействителен, мы делаем те же четыре вещи, которые мы делали при обязательной проверке ввода.

Последнее, что нам нужно проверить в нашей форме, это длина сообщения. Требуемая проверка уже была рассмотрена выше, поэтому все, что нам нужно сделать, это проверить длину сообщения:

1
//check minimum lengths
2
if(minLength){
3
    if( value.length < parseInt(minLength) ){
4
        this.focus();
5
        formok = false;
6
        errors.push(nameUC + errorMessages.minlength + minLength + ' charcters');
7
        return false;
8
    }
9
}

Нам не нужно использовать Modernizr здесь. Вместо этого все, что нам нужно сделать, это проверить, что у этого элемента установлена минимальная длина, и, если это так, убедиться, что его длина больше, чем его установленная минимальная длина. Length является свойством всех строковых объектов в JavaScript и возвращает количество символов в строке. Мы используем parseInt() для преобразования minLength в целое число, чтобы сравнить его со значением value.length. minLength было получено из атрибута data-minlength. Это значение извлекается в виде строки, поэтому, чтобы предотвратить возможные ошибки в будущем (сравнение строк с числами и т. д.), мы конвертируем его в целое число.

Наши полифилы и проверки завершены и отсортированы. Вы должны были получить следующий код:

1
//to ensure compatibility with HTML5 forms, we have to validate the form on submit button click event rather than form submit event. 
2
//An invalid html5 form element will not trigger a form submit.
3
formSubmitButton.bind('click',function(){
4
    var formok = true,
5
        errors = [];
6
        
7
    formElements.each(function(){
8
        var name = this.name,
9
            nameUC = name.ucfirst(),
10
            value = this.value,
11
            placeholderText = this.getAttribute('placeholder'),
12
            type = this.getAttribute('type'), //get type old school way
13
            isRequired = this.getAttribute('required'),
14
            minLength = this.getAttribute('data-minlength');
15
            
16
        //if HTML5 formfields are supported            
17
        if( (this.validity) && !this.validity.valid ){
18
            formok = false;
19
            
20
            //if there is a value missing
21
            if(this.validity.valueMissing){
22
                errors.push(nameUC + errorMessages.required);    
23
            }
24
            //if this is an email input and it is not valid
25
            else if(this.validity.typeMismatch && type == 'email'){
26
                errors.push(errorMessages.email + nameUC);
27
            }
28
            
29
            this.focus(); //safari does not focus element an invalid element
30
            return false;
31
        }
32
        
33
        //if this is a required element
34
        if(isRequired){    
35
            //if HTML5 input required attribute is not supported
36
            if(!Modernizr.input.required){
37
                if(value == placeholderText){
38
                    this.focus();
39
                    formok = false;
40
                    errors.push(nameUC + errorMessages.required);
41
                    return false;
42
                }
43
            }
44
        }
45
46
        //if HTML5 input email input is not supported
47
        if(type == 'email'){     
48
            if(!Modernizr.inputtypes.email){ 
49
                var emailRegEx = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/; 
50
                if( !emailRegEx.test(value) ){    
51
                    this.focus();
52
                    formok = false;
53
                    errors.push(errorMessages.email + nameUC);
54
                    return false;
55
                }
56
            }
57
        }
58
        
59
        //check minimum lengths
60
        if(minLength){
61
            if( value.length < parseInt(minLength) ){
62
                this.focus();
63
                formok = false;
64
                errors.push(nameUC + errorMessages.minlength + minLength + ' charcters');
65
                return false;
66
            }
67
        }
68
    });
69
    
70
    //if form is not valid
71
    if(!formok){
72
        
73
        //show error message here
74
        
75
    }
76
    //if form is valid
77
    else {
78
        
79
        //ajax request + show success message here
80
        
81
    }
82
    
83
    return false; //this stops submission off the form and also stops browsers showing default error message
84
});

Потрясающие! Мы почти у цели. На данный момент все, что нам нужно сделать, это написать код, который обрабатывает логику, чтобы проверить, должна ли форма быть отправлена или нет. Нам нужно будет отобразить наши сообщения об ошибках, которые мы сохранили, и прекратить отправку формы в случае ошибки. Если, с другой стороны, ошибки нет, мы отправляем форму через AJAX и показываем сообщение об успехе. Нам также нужно реализовать функцию ucfirst(), которую мы использовали для прописных букв первой буквы каждого имени поля.


Шаг 11: Почти там ...

Первое, что мы собираемся сделать, это написать функцию для обработки сообщений, а также нашу функцию ucfirst(). Вставьте следующий код за пределы формы formSubmitButton.bind ... логики, которую мы писали.

1
//other misc functions
2
function showNotice(type,data)
3
{
4
    if(type == 'error'){
5
        successNotice.hide();
6
        errorNotice.find("li[id!='info']").remove();
7
        for(x in data){
8
            errorNotice.append('<li>'+data[x]+'</li>');    
9
        }
10
        errorNotice.show();
11
    }
12
    else {
13
        errorNotice.hide();
14
        successNotice.show();    
15
    }
16
}
17
18
String.prototype.ucfirst = function() {
19
    return this.charAt(0).toUpperCase() + this.slice(1);
20
}

Функция showNotice будет принимать два аргумента.

  • Тип сообщения для отображения
  • Данные для отображения в сообщении.

Если типом является 'error', мы скрываем сообщение об успешном завершении, перебираем данные (которые должны быть массивом) и добавляем элементы списка к уведомлениям об ошибках UL. Затем мы показываем уведомление об ошибке с помощью функции jQuery show(). Поскольку весь наш код содержится в одном и том же блоке, мы имеем доступ к переменным, установленным вне этой функции (successNotice и errorNotice). Если мы хотим показать сообщение об успехе, мы просто скрываем сообщение об ошибке и отображаем сообщение об успехе.

О функции ucfirst() - я добавляю эту функцию в прототип строкового объекта.

«Прототип - это объект, от которого другие объекты наследуют свойства».

Это означает, что все строковые объекты будут наследовать нашу функцию ucfirst(). Вот почему ранее мы использовали name.ucfirst(). name является строкой, и поскольку наш метод находится в прототипе, он доступен для использования.

Мы получаем первый символ (charAt(0)), делаем его заглавным (toUpperCase()), а затем возвращаем его с остальной частью строки минус первый символ (slice(1)). charAt, toUpperCase и slice являются методами строкового объекта. Вы можете прочитать больше об объекте-прототипе здесь или здесь.

Теперь, когда у нас разобраны разные функции, мы можем сосредоточиться на логике обработки формы. Мы вернулись к работе внутри логики formSubmitButton.bind.

1
//if form is not valid
2
if(!formok){
3
    
4
    //show error message here
5
    
6
}
7
//if form is valid
8
else {
9
    
10
    //ajax request + show success message here
11
    
12
}

Мы начнем с логики, когда форма невалидна. Следующий код должен быть помещен внутри оператора if:

1
//animate required field notice
2
$('#req-field-desc')
3
    .stop()
4
    .animate({
5
        marginLeft: '+=' + 5
6
    },150,function(){
7
        $(this).animate({
8
            marginLeft: '-=' + 5
9
        },150);
10
    });
11
12
//show error message 
13
showNotice('error',errors);

Первый фрагмент кода просто анимирует «* указывает на обязательность поля». Это не обязательно; это просто фича, которая дает пользователю немного больше обратной связи - что проблема, по сути, возникла. Мы используем функцию jQuery animate() для анимации свойства CSS margin-left поля элемента. После этого мы будем вызывать нашу функцию showNotice(). Мы хотим показать сообщение об ошибке, поэтому мы передаем 'error' в качестве первого аргумента, а затем массив ошибок, в котором мы храним наши сообщения об ошибках проверки формы.

Если форма валидна, мы должны отправить ее через AJAX.

1
loading.show();
2
$.ajax({
3
    url: form.attr('action'),
4
    type: form.attr('method'),
5
    data: form.serialize(),
6
    success: function(){
7
        showNotice('success');
8
        form.get(0).reset();
9
        loading.hide();
10
    }
11
});

Во-первых, мы раскрываем наш 'загрузочный' gif, чтобы указать, что форма что-то делает. Затем мы используем функцию jQuery ajax() для отправки формы в process.php. Для url и type мы используем функцию jQuery attr(), чтобы получить эти атрибуты. Для данных мы используем функцию jQuery serialize(). Если запрос AJAX был успешным, мы вызываем нашу функцию showNotice() и передаем ей 'success' в качестве первого аргумента. Это отображает наше сообщение об успехе. Последнее, что мы делаем, это сбрасываем форму (очищаем поля формы) и скрываем gif загрузки. Все, о JavaScript теперь позаботились! Поздравляю! Вы должны были закончить с файлом script.js, должно быть похоже на это:

1
$(function(){
2
3
    //set global variables and cache DOM elements for reuse later
4
    var form = $('#contact-form').find('form'),
5
        formElements = form.find('input[type!="submit"],textarea'),
6
        formSubmitButton = form.find('[type="submit"]'),
7
        errorNotice = $('#errors'),
8
        successNotice = $('#success'),
9
        loading = $('#loading'),
10
        errorMessages = {
11
            required: ' is a required field',
12
            email: 'You have not entered a valid email address for the field: ',
13
            minlength: ' must be greater than '
14
        }
15
    
16
    //feature detection + polyfills
17
    formElements.each(function(){
18
19
        //if HTML5 input placeholder attribute is not supported
20
        if(!Modernizr.input.placeholder){
21
            var placeholderText = this.getAttribute('placeholder');
22
            if(placeholderText){
23
                $(this)
24
                    .addClass('placeholder-text')
25
                    .val(placeholderText)
26
                    .bind('focus',function(){
27
                        if(this.value == placeholderText){
28
                            $(this)
29
                                .val('')
30
                                .removeClass('placeholder-text');
31
                        }
32
                    })
33
                    .bind('blur',function(){
34
                        if(this.value == ''){
35
                            $(this)
36
                                .val(placeholderText)
37
                                .addClass('placeholder-text');
38
                        }
39
                    });
40
            }
41
        }
42
        
43
        //if HTML5 input autofocus attribute is not supported
44
        if(!Modernizr.input.autofocus){
45
            if(this.getAttribute('autofocus')) this.focus();
46
        }
47
        
48
    });
49
    
50
    //to ensure compatibility with HTML5 forms, we have to validate the form on submit button click event rather than form submit event. 
51
    //An invalid html5 form element will not trigger a form submit.
52
    formSubmitButton.bind('click',function(){
53
        var formok = true,
54
            errors = [];
55
            
56
        formElements.each(function(){
57
            var name = this.name,
58
                nameUC = name.ucfirst(),
59
                value = this.value,
60
                placeholderText = this.getAttribute('placeholder'),
61
                type = this.getAttribute('type'), //get type old school way
62
                isRequired = this.getAttribute('required'),
63
                minLength = this.getAttribute('data-minlength');
64
                
65
            //if HTML5 formfields are supported            
66
            if( (this.validity) && !this.validity.valid ){
67
                formok = false;
68
                
69
                //if there is a value missing
70
                if(this.validity.valueMissing){
71
                    errors.push(nameUC + errorMessages.required);    
72
                }
73
                //if this is an email input and it is not valid
74
                else if(this.validity.typeMismatch && type == 'email'){
75
                    errors.push(errorMessages.email + nameUC);
76
                }
77
                
78
                this.focus(); //safari does not focus element an invalid element
79
                return false;
80
            }
81
            
82
            //if this is a required element
83
            if(isRequired){    
84
                //if HTML5 input required attribute is not supported
85
                if(!Modernizr.input.required){
86
                    if(value == placeholderText){
87
                        this.focus();
88
                        formok = false;
89
                        errors.push(nameUC + errorMessages.required);
90
                        return false;
91
                    }
92
                }
93
            }
94
95
            //if HTML5 input email input is not supported
96
            if(type == 'email'){     
97
                if(!Modernizr.inputtypes.email){ 
98
                    var emailRegEx = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/; 
99
                     if( !emailRegEx.test(value) ){    
100
                        this.focus();
101
                        formok = false;
102
                        errors.push(errorMessages.email + nameUC);
103
                        return false;
104
                    }
105
                }
106
            }
107
            
108
            //check minimum lengths
109
            if(minLength){
110
                if( value.length < parseInt(minLength) ){
111
                    this.focus();
112
                    formok = false;
113
                    errors.push(nameUC + errorMessages.minlength + minLength + ' charcters');
114
                    return false;
115
                }
116
            }
117
        });
118
        
119
        //if form is not valid
120
        if(!formok){
121
            
122
            //animate required field notice
123
            $('#req-field-desc')
124
                .stop()
125
                .animate({
126
                    marginLeft: '+=' + 5
127
                },150,function(){
128
                    $(this).animate({
129
                        marginLeft: '-=' + 5
130
                    },150);
131
                });
132
            
133
            //show error message 
134
            showNotice('error',errors);
135
            
136
        }
137
        //if form is valid
138
        else {
139
          loading.show();
140
            $.ajax({
141
                url: form.attr('action'),
142
                type: form.attr('method'),
143
                data: form.serialize(),
144
                success: function(){
145
                    showNotice('success');
146
                    form.get(0).reset();
147
                    loading.hide();
148
                }
149
            });
150
        }
151
        
152
        return false; //this stops submission off the form and also stops browsers showing default error messages
153
        
154
    });
155
156
    //other misc functions
157
    function showNotice(type,data)
158
    {
159
        if(type == 'error'){
160
            successNotice.hide();
161
            errorNotice.find("li[id!='info']").remove();
162
            for(x in data){
163
                errorNotice.append('<li>'+data[x]+'</li>');    
164
            }
165
            errorNotice.show();
166
        }
167
        else {
168
            errorNotice.hide();
169
            successNotice.show();    
170
        }
171
    }
172
    
173
    String.prototype.ucfirst = function() {
174
        return this.charAt(0).toUpperCase() + this.slice(1);
175
    }
176
    
177
});

Заключение

Поздравляю! Вы сделали это. Это была долгая поездка, и мы рассмотрели много вопросов в этом уроке.

Итак, куда вы идете дальше? Этот пример может быть расширен до гораздо большей формы, и весь код, который вы написали, будет работать без нареканий. Вы даже можете добавить свою собственную проверку для таких вещей, как поле ввода телефона или атрибут maxlength!

Спасибо за чтение, и я надеюсь, что вам понравился этот урок!

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Web Design tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.