Отправка произвольной формы на AJAX без перезагрузки страницы
08-06-2018Reading time ~ 11 min.jQuery и JavaScript, PHP 57941
Технология AJAX используется для того, чтобы с помощью javascript отправлять «фоновые» http-запросы, что позволяет не перезагружать страницу, как это происходит при обычной отправке форм.
Реализация AJAX довольно сложна с технической точки зрения. На самом деле происходит работа API XMLHttpRequest, который и выполняет всю работу. Чтобы упростить код, используются функции-«обертки», которые освобождают нас от сложного программирования XMLHttpRequest. В jQuery для этого используются функци ajax
, а также её «модификация» post
. При этом, если jQuery не нужна, то в Сети можно найти другие библиотеки, реализующие функционал AJAX, например Selector (которая вообще выступает как легковесная замена jQuery).
Не зависимо от используемой библиотеки, основные принципы отправки даных с помощью Аякса будут едиными. Я буду всё показывать на примере jQuery. В конце статьи вы найдете ссылку на zip-архив со всеми примерами.
Что можно отправлять через AJAX
Многие думают, что отправке подлежит только форма. Но на самом деле, отправлять можно любые данные. По большому счёту форма как таковая даже не нужна, можно разместить поля ввода (input, textarea и т.п.) в любых местах страницы: перед отправкой просто сформировать из них данные.
На практике стараются использовать именно форму, поскольку это значительно упрощает формирование данных: перед отправкой они просто кодируются (серилизуются) и в таком виде отправляются на сервер.
Общая схема работы
Почти всегда это происходит так:
- На html-странице создается форма с нужными полями.
- Перед отправкой, данные формы серилизуются и данные уходят к php-обработчику на сервере.
- Обработчик, выполнив свою работу, возвращает ответ. Это может быть какие-то данные или готовый html-код.
- Этот ответ размещается в заданном блоке на странице.
Дополнительно, при получении ответа, часто скрывают форму, чтобы исключить её повторную отправку.
Начальный код
Вначале сформируем каркас HTML. Я использую Berry CSS с готовыми CSS-классами. Также подключаю jQuery в секцию HEAD.
<!DOCTYPE HTML> <html lang="ru"><head> <meta charset="UTF-8"> <title>Отправка формы на AJAX</title> <link rel="stylesheet" href="assets/css/style.css"> <script src="assets/js/jquery.min.js"></script> </head><body> ... тут будет наш код ... <link rel="stylesheet" href="assets/css/-lazy.css"> <link rel="stylesheet" href="assets/css/fontawesome.css"> </body></html>
Теперь создадим простую форму и блок ответа.
<form id="my_form"> <div class="mar10-tb"><label>Имя: <input name="f[name]"></label></div> <div class="mar10-tb"><label>Телефон: <input name="f[phone]"></label></div> <div><button type="submit">Отправить</button></div> </form> <div id="my_message"></div>
Первые особенности
В первую очередь обратите внимание, что форма не имеет привычных атрибутов method и action. Формально никто не мешает их указать, но в реальности они будут использоваться только, если будет обычная, а не аякс-отправка. При аякс-отправке нам нужны будут лишь данные формы, а метод и адрес отправки задаются уже в js-коде. Из-за этого мы используем только атрибут id, по которому и сможем идентифицировать форму.
Кнопка имеет тип submit. На самом деле это не обязательно, поскольку главное это просто как-то отреагировать на нажатие кнопки, а это может быть не только submit, но и click. Тип submit удобен тем, что кнопка располагается внутри формы, а значит можно будет перехватить её отправку без дополнительной идентификации.
Но, если кнопку разместить вне формы (если стоит такая задача), то submit сработает не совсем корректно. Особенно если на странице несколько форм, возникнет неопределенность. В этом случае тип кнопки определяют как button (то есть она ничего не делает), и при этом присваивают ей идентификатор id. Например так:
<div><button type="button" id="my_form_send">Отправить</button></div> <form id="my_form"> <div class="mar10-tb"><label>Имя: <input name="f[name]"></label></div> <div class="mar10-tb"><label>Телефон: <input name="f[phone]"></label></div> </form> <div id="my_message"></div>
Дальше в js-коде нужно будет перехватывать не submit (формы), а click (кнопки).
Далее. Поля формы заданы в виде «массива» f
. В php-обработчике мы получим сразу упорядоченные данные.
Блок #my_message служит для вывода ответа.
Если используется форма, то лучше всё-таки использовать именно submit, поскольку отправка формы может происходить и без нажатия на кнопку: Enter на любом текстовом input-поле. Это типовое поведение браузеров.
Первый вариант (submit)
При отправке формы при нажатии на submit-кнопку формы, возникает одноименное событие, которое мы и перехватываем. На jQuery это будет выглядеть так:
$('#my_form').submit(function(){ alert('Нажата submit-кнопка'); return false; });
Чтобы заблокировать отправку и перезагрузку страницы, функция возвращает false
. Саму же обработку мы разместим вместо alert()
, но перед этим разберемся что предлагает jQuery для отправки аякс-запроса.
Функции для AJAX
Функция $.ajax очень мощная и содержит массу параметров и возможностей. В «типовом» варианте она используется примерно так:
$.ajax({ method: "POST", url: "some.php", data: { name: "John", location: "Boston" } }) .done(function( msg ) { alert( "Data Saved: " + msg ); });
То есть указывается тип запроса (method), адрес обработчика (url), данные (data, обычно в JSON-формате), а также функции, которые реагируют на ответ (в этом примере done).
На практике же гораздо удобней использовать другую функцию — $.post
:
$.post( адрес обработчика, отправляемые данные, function(msg) { ответ сервера } );
Функция $.post эквивалентна $.ajax
с предопределенными параметрами.
Привожу полный код, чтобы вы не запутались:
$('#my_form').submit(function(){ $.post( 'post.php', // адрес обработчика $("#my_form").serialize(), // отправляемые данные function(msg) { // получен ответ сервера $('#my_form').hide('slow'); $('#my_message').html(msg); } ); return false; });
Данные мы отправляем серилизованные с помощью функции serialize()
, а в ответной функции скрываем форму (hide()), а в #my_message выводим ответ сервера.
Обработчик размещаем в файле post.php в таком виде:
<?php echo '<b class="t-green">Это ответ сервера</b>';
То есть он пока ничего не делает, а просто выдаёт текст.
Здесь важный момент: адрес обработчика указывается так, чтобы он был доступен по http-адресу. Грубо говоря, адрес можно вбить в браузер. Это особенно актуально, когда используется сложная структура страниц или js-файл выносится в отдельный каталог. В некоторых случаях оптимальным будет указать полный http-адрес к php-файлу, например http://сайт/post.php. Перед тем, как работать с аякс-запросом, убедитесь в корректности адреса обработчика, иначе запрос уйдёт «в никуда», а вы будете гадать, почему он не работает. :-)
Ответ от сервера мы получаем в виде простого текста, поэтому мы его просто выводим в блоке #my_message как есть.
Второй вариант (button)
Теперь рассмотрим второй вариант — отправку данных с помощью произвольного элемента по клику.
$('#my_form_send').click(function(){ $.post( 'post.php', // адрес обработчика $("#my_form").serialize(), // отправляемые данные function(msg) { // получен ответ сервера $('#my_form').hide('slow'); $('#my_message').html(msg); } ); });
Как видите, принципиально здесь ничего не поменялось, кроме того, что мы отслеживаем click на кнопке #my_form_send (которая не обязательно может быть кнопкой, а например блоком span).
Какой вариант использовать, зависит от задач вёрстки. В некоторых случаях кнопку отправки нужно вынести за пределы формы и в этом случае придется использовать click-вариант.
PHP-обработчик
Давайте немного изменим обработчик, чтобы он выдавал какую-то отладочную информацию, например входящий post-запрос.
echo '< pre>'; print_r($_POST); echo '< /pre>';
Еще раз отправив форму мы увидим содержимое $_POST
:
Array ( [f] => Array ( [name] => Вася [phone] => 09 ) )
Здесь без труда угадываются поля форм.
Произвольные данные для отправки
Отправка данных происходит в специальном (серилизованном) формате. Функция jQuery serialize() собственно и приводит данные формы к этому формату, а на стороне сервера, PHP сам сформирует обычный массив. Всё это происходит в фоновом режиме, поэтому дополнительные преобразования не нужны.
Рассмотрим вариант, когда нужно отправить не форму, а какие-то произвольные данные, наример массив данных.
Если вместо фрагмента
$("#my_form").serialize(),
указать
'текст для отправки',
то php-обработчик покажет пустой массив. То есть данные не прошли. Для того, чтобы данные были получены их следует оформить:
'key1=value1&key2=value2',
или
{key1: 'value1', key2: 'value2'},
Более удобным (и распростанённым) будет второй вариант который представляет собой привычный js-объект/массив:
$('#my_form_send2').click(function(){ $.post( 'post.php', // адрес обработчика { key1: 'value1', key2: 'value2' }, function(msg) { // получен ответ сервера $('#my_form2').hide('slow'); $('#my_message2').html(msg); } ); });
Сервер получит:
Array ( [key1] => value1 [key2] => value2 )
Таким нехитрым способом можно отправлять любые данные на сервер, при том, что от формы можно вообще отказаться. Приведу пример, где все поля ввода могут быть расположены произвольно, при том, что отправка происходит по клику на ссылке:
<div class="mar10-tb"><label>Имя: <input name="f[name]" id="i1"></label></div> <div class="mar10-tb"><label>Телефон: <input name="f[phone]" id="i2"></label></div> <div><a href="#" id="my_send3">Отправить</a></div> <div id="my_message3"></div> <script> $('#my_send3').click(function(){ $.post( 'post.php', // адрес обработчика { name: $('#i1').val(), phone: $('#i2').val() }, function(msg) { // получен ответ сервера $('#my_message3').html(msg); } ); return false; }); </script>
Приём сложных данных от сервера
Теперь рассмотрим случай, когда сервер должен вернуть не просто текст, а какие-то данные, например массив.
Для этого в php-скрипте следует отдавать специально преобразованный в JSON-формат php-массив. К счастью разработчики PHP всё уже предусмотрели. :-)
Пусть у нас обработчик будет post-json.php
:
// результирующий массив $json = array( 'key1' => 'value1', 'key2' => 'value2', 'key3' => 'value3', ); // отдаем echo json_encode($json);
То есть на входе обычный php-массив, который кодируется функцией json_encode()
. Теперь, в сообщении на странице мы увидим:
{"key1":"value1","key2":"value2","key3":"value3"}
Это обычная строка, которую нужно преобразовать в js-объект. Для этого случая есть несколько разных вариантов, но самый лучший, это задействовать последний параметр функции $.post
, который указывает на тип возвращаемых данных. В нашем случае этот тип json
, а значит результат будет автоматически преобразован так, как нам нужно.
$('#my_send3').click(function(){ $.post( 'post-json.php', // адрес обработчика { name: $('#i1').val(), phone: $('#i2').val() }, function(msg) { // получен ответ сервера — это json! console.log(msg); // в консоль для контроля $('#my_message3').html(msg.key1); }, 'json' // тип ответа ); return false; });
Здесь мы добавили тип ответа, поэтому переменная msg
уже не текст, а готовый js-объект, с которым мы и работаем соответствующим образом. Для контроля можно использовать Консоль браузера (F12 в FireFox) с помощью console.log()
.
Обработка входящих данных на сервере
Теперь рассмотрим вопросы приёма данных на сервере. Поскольку обработчик доступен прямо из браузера, то его следует как-нибудь защитить от прямого вызова. Полноценно сделать это можно в рамках CMS или php-фреймворка, вроде LPF, поэтому я покажу лишь минимальные приёмы.
В первую очередь следует заблокировать работу скрипта, если нет входящих POST-данных. В начале php добавьте такой код:
if (!$_POST) exit('No direct script access allowed');
Теперь при прямом вызове этого скрипта, будет выдаваться сообщение и работа скрипта будет прекращена.
Во вторую очередь следует проверять все входящие данные из формы и по возможности их так обрабатывать, чтобы исключить возможные проблемы безопасности. Сама форма должна использовать HTML-атрибуты для полей, чтобы обеспечить первоначальную валидацию данных. Например, если поле предназначено для электропочты, то следует указать тип email — браузер не позволит выполнить отправку неверных данных.
В некоторых случаях желательно использовать различные jQuery-плагины, который позволяют отформатировать ввод данных. Например ввод телефона в виде групп цифр. Я затрагиваю эти вопросы, поскольку он достаточно важны, но в своих примерах я их опускаю, чтобы не загромождать код. :-)
Отправка email-почты
Это достаточно типовая задача, которая решается через submit-отправку. Пусть будет простая форма из двух полей.
<form id="my_form_email"> <div class="mar10-tb"><label>Имя: <input type="text" name="f[name]" required></label></div> <div class="mar10-tb"><label>Email: <input type="email" name="f[email]" required></label></div> <div><button type="submit">Отправить</button></div> </form> <div id="my_message_email"></div> <script> $('#my_form_email').submit(function(){ $.post( 'post-email.php', // адрес обработчика $("#my_form_email").serialize(), // отправляемые данные function(msg) { // получен ответ сервера $('#my_form_email').hide('slow'); $('#my_message_email').html(msg); } ); return false; }); </script>
Обработчик у нас в post-email.php. На входе ($_POST) получаются такие данные:
Array ( [f] => Array ( [name] => Вася [email] => va@saj.com ) )
Поскольку все данные в массиве с ключом f
, то мы вводим ещё одно условие — если такого ключа нет, то это ошибочные данные.
if (!isset($_POST['f'])) exit('No direct script access allowed');
Аналогично можно поступить и с другими полями формы. Поскольку они являются обязательными, то форма всегда должна их содержать.
if (!isset($_POST['f']['name'])) exit('No direct script access allowed'); if (!isset($_POST['f']['email'])) exit('No direct script access allowed');
Дальше вынесем входящие данные в отдельные переменные:
$email = trim(strip_tags($_POST['f']['email'])); $name = trim(strip_tags($_POST['f']['name']));
С помощью trim()
удаляем возможные крайние пробелы, а strip_tags()
удаляет все html/php-тэги.
Дальше следует проверить входящие данные. В первую очередь верность email-адреса. Вполне возможна ситуация, когда посетитель использовал старую версию браузера, которая не понимает нужный тип поля ввода. Если адрес ошибочный, то следует выдать предупреждение и выйти из php-обработчика.
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { exit ('Неверный email! Обновите страницу (F5) и укажите правильный адрес'); }
Аналогично поступаем с именем, если оно оказалось пустым.
if (!$name) { exit('Не указано имя! Обновите страницу (F5) и укажите своё имя'); }
Вот теперь все проверки пройдены и можно выполнить непосредственную отправку почты. Я приведу полный код post-email.php:
if (!$_POST) exit('No direct script access allowed'); if (!isset($_POST['f'])) exit('No direct script access allowed'); if (!isset($_POST['f']['name'])) exit('No direct script access allowed'); if (!isset($_POST['f']['email'])) exit('No direct script access allowed'); $email = trim(strip_tags($_POST['f']['email'])); $name = trim(strip_tags($_POST['f']['name'])); if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { exit('Неверный email! Обновите страницу (F5) и укажите правильный адрес'); } if (!$name) { exit('Не указано имя! Обновите страницу (F5) и укажите своё имя'); } $to = 'ваш@email'; // адрес получателя $subject = 'Тема письма'; // тема письма // формируем тело сообщения $message = 'Имя: ' . $name . "\r\n" . 'Email: ' . $email; // формируем headers для письма $headers = 'From: '. $email . "\r\n"; // от кого // кодируем заголовок в UTF-8 $subject = preg_replace("/(\r\n)|(\r)|(\n)/", "", $subject); $subject = preg_replace("/(\t)/", " ", $subject); $subject = '=?UTF-8?B?' . base64_encode($subject) . '?='; // отправка @mail($to, $subject, $message, $headers); echo 'Спасибо, ваше сообщение отправлено!';
Вы можете использовать данный пример как заготовку для своих задач.
Спасибо Макс, отличный материал, сохранил в шпаргалки.
спс! очень помогло!
Отлично!!! Очень помогло
Долго искал решение, статья очень помогла! Спасибо огромное!
Все понятно и доступно!!!
Из нескольких до этого попробованных Ваш только заработал!
а на ванильном JS как такое сделать?
отправить не-json данные
Вот здесь есть без jQuery: https://maxsite.org/page/alpine-ajax Только цепляетесь на какое-то событие для отправки.
большое спасибо!
Статья очень неплохая, но было бы неплохо два практических примера, это запись в файл формата json и запись в базу данных информации из формы без перезагрузки. А так, что сказать... Данные из формы на страницу без перезагрузки легко добавить безо всяких юквери и аякса. Подобных примеров в интернете - десятки, а вот таких о которых я упомянул - нет ни одного)