Артём Мáлков

Форма обратной связи с отправкой на почту без перезагрузки страницы

20 апр22 комм

Обратная связь – довольно востребованный, на мой взгляд, функционал, присутствующий, опять же по-моему скромному мнению, почти на каждом сайте. Под обратной связью я подразумеваю старую добрую форму для написания сообщений администрации сайта (пример именно такой формы мы будем здесь рассматривать), форму обратного звонка (она же «Перезвоните мне»), простой вариант подписки на новости и прочие подобные.

За свою «карьеру» мне довелось видеть разные реализации данного функционала. В каких-то случаях я не понимал, за чем всё так усложнять, а где-то, наоборот, недоумевал, почему не предусмотрели такой, казалось бы, банальный поворот событий. Шло время, и накопившееся возмущение с некоторым приобретенным опытом вылилось ни во что иное, как в своё решение, о котором я сегодня подробно расскажу.

Простая форма обратной связи для сайта на HTML+jQuery+PHP

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

Чтобы удостовериться в том, что работа функции никак не ограничена, создайте в корне сайта PHP-файл со следующим содержимым:

<?php

	var_dump(mail('Ваша почта', 'Test mail', 'Test'));

Откройте его в браузере, и если вы видите сообщение:

bool(true)

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

Итак, мы убедились, что PHP-функция mail() у нас отправляет тестовое письмо, значит, пора начинать формировать нашу форму обратной связи, и, как это ни странно, начнём мы с её верстки (HTML). Выглядит она следующим образом:

<input type="text" id="formName" value="" maxlength="100" placeholder="Ваше имя" />


<input type="text" id="formEmail" value="" maxlength="100" placeholder="Ваше e-mail *" />


<textarea id="formMessage" maxlength="1000" placeholder="Сообщение *"></textarea>

<div class="formResult"></div>

<button class="formSubmit">Отправить</button>

Я не поместил форму в логичный тег <form>, поскольку данные с неё я будут отправлять AJAX'ом (к слову, AJAX позволит нам отправлять форму без перезагрузки страницы), а потому не задействую некоторые ненужные, на мой взгляд, составляющие. Обязательность полей, в отличие от классической формы, забегая немного вперед, мы будем указывать в обработчике (PHP-скрипте). Допустимую длину полей указываем здесь и дополнительно в том же обработчике. Новые поля двух приведённых в примере типов (текст и текстовая область) добавляются по аналогии, уникальность поля задаётся через атрибут id (в нашем случае это formName, formEmail и formMessage).

Хорошо, с вёрсткой мне кажется всё более или менее понятно, идём дальше. Скрипт, который будет собирать и отправлять данные с нашей формы (необходимо наличие jQuery на вашем сайте):

$(document).ready(function() {
	
	$(".formSubmit").on("click", function() {
		
		$.ajax({
			url: "/form.php", // Куда отправляем данные (обработчик)
			type: "POST", // Тип запроса
			dataType: "json",
			data: {
				"name": $("#formName").val(), // Имя
				"email": $("#formEmail").val(), // E-mail
				"message": $("#formMessage").val() // Сообщение
			},
			success: function(data) {
			
				$(".formResult").html(data); // Выводим результат
				
			}
		});
		
	});
	
});

Я считаю также понятным. Если нет – пишите в комментариях, постараюсь дать пояснения.

Завершающая часть (PHP), та, которая будет получать данные с формы, обрабатывать их и отправлять нужным адресатам (да-да, их здесь может быть несколько):

header('Content-Type: application/json; charset=utf-8');

$mailAdress = [
	'Ваша почта'
]; // Массив получателей (E-mail)

$mailSubject = 'Письмо с обратной связи'; // Тема письма

/*
	
	Массив полей:

		code - название поля из AJAX
		title - подпись
		length - длина поля
		required - обязательное или нет (true или false)

*/

$fieldsArr = [
	[
		'code' => 'name',
		'title' => 'Имя',
		'length' => 100,
		'required' => false
	], [
		'code' => 'email',
		'title' => 'E-mail',
		'length' => 100,
		'required' => true
	], [
		'code' => 'message',
		'title' => 'Сообщение',
		'length' => 1000,
		'required' => true
	]
];

if(isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest') {

	$errorList = []; // Список ошибок

	$mailBody = '';

	foreach($fieldsArr as $field) {

		if(!isset($_POST[$field['code']])) {

			// Если одно из полей не передано (не путать с пустым значением) - останавливаем работу скрипта

			die();

		} elseif(trim($_POST[$field['code']]) == '' && $field['required']) {

			// Если передано пустое поле и оно обязательное для заполнения - формируем сообщение об ошибке

			$errorList[] = 'Поле <b>'.$field['title'].'</b> обязательно для заполнения.';

		} else {

			// Формируем сообщение отправляемое на почту

			$mailBody .= '<p><b>'.$field['title'].':</b> '.mb_substr(strip_tags(trim($_POST[$field['code']])), 0, $field['length']).'</p>';

		}

	}

	if(!empty($errorList)) {

		// Если ошибки есть, выводим их

		echo json_encode(implode('<br />', $errorList), JSON_UNESCAPED_UNICODE);

	} else {

		$domain = $_SERVER['HTTP_HOST'];

		if(substr($domain, 0, 4) == 'www.') {
			
			$domain = substr($_SERVER['HTTP_HOST'], 4);
		
		}

		$mailHeaders = 'MIME-Version: 1.0'."\r\n";
		$mailHeaders .= 'Content-type: text/html; charset=utf-8'."\r\n";
		$mailHeaders .= 'From: Система уведомлений <no-reply@'.$domain.'>'."\r\n";

		foreach($mailAdress as $mail) {

			mail($mail, $mailSubject, $mailBody, $mailHeaders);

		}

		echo json_encode('Сообщение успешно отправлено!', JSON_UNESCAPED_UNICODE);

	}

} else {

	die();

}

Здесь, я думаю, мне определенно стоит дать некоторые комментарии.

1. Как вы помните, адресатов (получателей) писем может быть несколько. Здесь все они пишутся в массиве. Пример нескольких адресатов:

$mailAdress = [
	'E-mail 1',
	'E-mail 2',
	'E-mail 3'
]; // Массив получателей (E-mail)

2. Все письма с сайта отсылаются от лица no-reply@домен вашего сайта, поэтому, чтобы письма не попадали в спам и в принципе отсылались, создайте этот адрес (о том, как создать корпоративную почту для домена, читайте в других наших постах) или замените на другой.

3. В массиве полей должны быть указаны коды (code) тех полей, которые вы передаете в AJAX.

Форма обратной связи с отправкой на почту без перезагрузки страницы

Если какое-то из описанных полей не передаётся, то обработчик (PHP-скрипт) остановит своё выполнение.

4. Текст отправляемого письма формируется автоматически на основе указанного массива полей.

Это, так скажем, самые важные моменты, остальное – не требует вашего участия.

Форма не идеальна, но вполне хорошая основа для будущих разработок. Буду рад почитать ваши мнения относительно неё. Чтобы сильно не раздувать пост, в комментариях дам несколько советов по тому, как эту форму можно доработать.

Форма (виджет) обратного звонка для сайта

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

Рекомендуем к просмотру
Как удалить теги p и br из Contact Form 7 в WordPress?
Статьи и советы
Как удалить все HTML-теги из строки на PHP/JavaScript?
Статьи и советы
Как остановить выполнение PHP-скрипта?
Статьи и советы
22
комментария
Форма комментирования этого поста скрыта. Авторизуйтесь, чтобы расширить привилегии гостевого посещения и получить необходимую помощь от сообщества Pandoge.
    • 0
    1066
      •  Команда Pandoge
    26 мар в 17:30

    Под этим комментарием дам несколько рекомендаций по доработке этот формы.

      • 0
      1066
        •  Команда Pandoge
      изменено 26 мар в 17:34

      Выполнить действие после успешной отправки формы.

      Сразу после строки:

      $(".formResult").html(data);

      добавьте:

      if(data == "Сообщение успешно отправлено!") {
      	
      	// Нужное действие
      	
      }

      В качестве нужного действия может выступать цель Яндекс.Метрики / Google Analytics, или же, например, очистка всех полей:

      $("#formName, #formEmail, #formMessage").val("");
    • 0
    2
      •  Пользователь
    6 фев в 13:51

    Как осуществить проверку checkbox? Всё время значение 'on'

    index.html:

    ...

    <input type="checkbox" name="checkbox" class="" id="checkbox" required>

    ...

    var checkbox = $("#checkbox").val();

    // checkbox

    ...

    checkbox": checkbox,

    ...

    form.php:

    ...

    if(!isset($_POST["name"]) || !isset($_POST["email"]) || !isset($_POST["phone"]) || !isset($_POST["checkbox"])) {

    ...

    $checkbox = $_POST["checkbox"];

    // checkbox

    ...

    И как сюда подключить recaptha2 от google?

    Спасибо.

      • 2
      1066
        •  Команда Pandoge
      6 фев в 15:12

      Дмитрий, здравствуйте!

      var checkbox = $("#checkbox").val();

      замените на:

      var checkbox = $("#checkbox").is(":checked");

      Возможные значения переменной checkbox при этом - true или false.

      На счет recaptha2 - не задавался таким вопросом, т. к. боты (на моем опыте) не докучают в такой форме.

      • 5
      2
        •  Пользователь
      6 фев в 17:25

      Артём Мáлков, Чек-бокс заработал, большое спасибо!

    • 0
    1066
      •  Команда Pandoge
    19 сен в 15:06

    Михаил Безденежный, Здравствуйте.

    Мне кажется, это не совсем ошибка, а больше предупреждение. Вероятнее всего, вы отправляете большой текст/файл. Увеличьте значение директивы client_body_buffer_size на вашем хостинге/сервере и попробуйте отправить письмо с вашим содержимым еще раз.

    • 1
    1
      •  Пользователь
    19 сен в 14:10

    Возникает вот такая ошибка:

    2019/09/19 13:57:58 [notice] 15762#15762: *7216031 a client request body is buffered to a temporary file /var/cache/nginx/client_temp/0032964712, client: 85.93.60.215, server: zarabotok.darkhost.pro, request: "POST /form.php HTTP/1.1", host: "zarabotok.darkhost.pro", referrer: "http://zarabotok.darkhost.pro/kontakt.htm"

    Что делать?

    • 1
    1066
      •  Команда Pandoge
    2 авг в 19:09

    Владислав, если у Вас уже есть модальное окно, то просто вставляете форму в него. Сам процесс создания такого окна будет (возможно) позже.

    Чтобы форма работала через form нужно переделывать работу скрипта. В Вашем случае, проще немного подправить стили.

    • 0
    1
      •  Пользователь
    2 авг в 18:42

    Я видел, кто-то спрашивал, как уместить этот код в модальное окно. У меня аналогичный вопрос.

    И как это всё можно запихнуть в <form>? Просто стили у меня под неё, если возможно, подскажите. Буду очень признателен

    • 1
    1
      •  Пользователь
    25 июн в 20:53

    Большое вам спасибо за этот скрипт! Я перелопатил огромную кучу сайтов и корректно всё стало работать только с вашим скриптом!!!!)

    • 1
    1066
      •  Команда Pandoge
    20 апр в 15:07

    Статья обновлена.

    Упрощен код + добавлена фильтрация данных.

    • 2
    1066
      •  Команда Pandoge
    23 окт в 19:44

    Никита, если нужен текст, то так:

    var select = $("#select_id option:selected").text();

    Если нужен value, то:

    var select = $("#select_id option:selected").val();

    Где #select_id - ID или класс селекта. В скрипте все так же:

    data: {
    
    	"name" : name,
    	"email" : email,
    	"message" : message,
    	"select": select 
    
    },

    В обработчике - $select = $_POST["select"];

    • 0
    766
      •  Гости
    23 окт в 17:24

    всё круто, спасибо, но непонятно как передавать инпут типа селект ?!?

    • 0
    766
      •  Гости
    29 сен в 07:27

    а как создать форму отправки для определённого региона?

    • 1
    1066
      •  Команда Pandoge
    11 июл в 13:16

    Аркадий, в HEAD сайта подключите:

    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>

    Проблема должна решиться smiley

    • 0
    766
      •  Гости
    11 июл в 06:02

    Ничего не работает.

    Браузер говорит о ошибке Uncaught ReferenceError: $ is not defined

    at (index):16

    Указывает на первую строку скрипта - $(document).ready(function() {

    Как с этим бороться?

    • 0
    1066
      •  Команда Pandoge
    7 июл в 13:36

    Сергей, Строку

    $msg_result = "Сообщение успешно отправлено!"; // Сообщение об успешной отправке

    Замените на

    $msg_result = "Сообщение успешно отправлено!<script>$('.input_1, .input_2').val('');</script>"; // Сообщение об успешной отправке

    Где .input_1, .input_2 - классы полей, которые нужно очистить.

    • 1
    766
      •  Гости
    7 июл в 02:45

    Артем Мáлков ,а как можно очистить поля после успешной отправки формы?

    • 5
    1066
      •  Команда Pandoge
    11 янв в 02:36

    Вадим Агеев, у Вас уже есть модальное окно или Вам его нужно создать?

    • 0
    766
      •  Гости
    10 янв в 18:23

    А как эту форму поместить модальное окно?

    • 1
    766
      •  Гости
    30 ноя в 15:11

    Пишет сообщение отправлено, но на почту не приходит. Адрес указан верный.

      • 6
      1066
        •  Команда Pandoge
      30 ноя в 18:22

      Николай, на хостинге ограничений нет? Папку "спам" проверяли?

Подняться наверх
«Pandoge» - помощник вебмастера