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

Зачем нужен callback-виджет?
Есть масса сервисов, которые предоставляют различные настраиваемые виджеты обратного звонка — с возможностью подключить АТС, интегрировать с CRM (amoCRM, Битрикс24), с оповещением в Telegram, по СМС и так далее. За такой богатый функционал нужно платить ежемесячно, покупать минуты или выбирать тариф по карману. Стоимость подобных виджетов стартует примерно от 200 рублей в месяц.
Используя виджет, о котором мы поговорим в этой статье, вы получите навечно бесплатное, независимое и настраиваемое под любые нужды решение — с возможностью отправки заявки на почту, в Telegram и по СМС. Также с помощью вебхуков можно подключить отправку данных в CRM (если есть такая возможность на стороне самой CRM).
Минус такого виджета – отсутствие возможности онлайн-звонка. Но, думаю, это не большой минус, а скорее плюс, так как часто менеджеры не успевают ответить на звонок, и компания получает негатив от потенциального клиента. А в случае с виджетом мы принимаем заявку, выводим сообщение о том, что перезвоним в течение определенного времени, и у клиента не возникает негатива. Поэтому минус виджета может быть и плюсом.
Итак, приступим к созданию виджета.
Разметка HTML + CSS
<div class="widget-callback">
<div class="callback-button">
<span class="callback-button-title">Вам перезвонить?</span>
<span class="callback-button-phone"></span>
</div>
<div class="callback-form">
<form id="calbback-widget-form">
<span class="callback-form-title">Оставьте свой телефон и мы свяжемся с вами</span>
<input type="tel" name="wgphone" placeholder="+7 (999) 999 99 99" required >
<input type="hidden" name="wgdata" value="Обратный звонок">
<input type="hidden" name="wgpage" value="<?php echo "https://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]"; ?>">
<input type="submit">
</form>
</div>
</div>
Так как виджет «сквозной» и устанавливается на все страницы, нам необходимо понимать, с какой страницы был заказан обратный звонок, чтобы оперативно помочь клиенту. Для этого нам понадобится скрытое поле в форме, которое будет передавать эту страницу, значение поле должно быть таким:
<?php echo "https://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]"; ?>
Если у вас одностраничный сайт или несколько страниц на HTML, измените расширение файла с .html на .php. Если устанавливаете на CMS , то все в порядке.
Второе скрытое поле будет передавать тему заявки — в данном случае это «Обратный звонок».
Теперь добавим небольшой скрипт открытия формы по клику на кнопку. Для этого нам понадобится подключить библиотеку jQuery и написать небольшой скрипт отправки. Если у вас уже подключена библиотека, то этого делать не стоит.
<script>
$(document).ready(function () {
//Открытие виджета по клику
jQuery('body').on('click', '.callback-button-phone', function (e) {
e.preventDefault();
jQuery('.widget-callback').toggleClass('widget-callback-form-open');
});
});
</script>
Далее оформим все с помощью CSS. Вы можете добавить этот код в свой файл стилей или создать новый.
body {
margin: 0;
padding: 0;
}
.widget-callback {
font-family: sans-serif;
font-size: 14px;
}
.widget-callback>div {
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
.widget-callback input {
outline: none !important
}
.widget-callback .callback-button {
position: absolute;
right: 60px;
bottom: 30px;
}
.widget-callback .callback-button-title {
position: absolute;
left: -150px;
top: 16px;
background: rgba(41, 41, 41, 0.75);
color: #fff;
padding: 6px 10px;
border-radius: 3px;
}
.widget-callback.widget-callback-form-open .callback-button-title {
display: none;
}
.widget-callback .callback-button-title:before {
content: '';
position: absolute;
width: 0;
height: 0;
border: solid transparent;
border-width: 6px;
top: 50%;
right: -12px;
transform: translateY(-50%);
border-left-color: rgba(41, 41, 41, 0.75);
}
.widget-callback .callback-button-phone {
width: 60px;
height: 60px;
display: block;
background: #199c68;
border-radius: 50%;
position: relative;
cursor: pointer;
animation: 1200ms ease 0s normal none 1 running shake;
animation-iteration-count: infinite;
-webkit-animation: 1200ms ease 0s normal none 1 running shake;
-webkit-animation-iteration-count: infinite;
}
.widget-callback.widget-callback-form-open .callback-button-phone {
animation: unset;
-webkit-animation: unset;
background: #ddd;
}
.widget-callback .callback-button-phone:before {
content: '';
background: url(call.svg);
background-size: contain;
position: absolute;
display: block;
width: 24px;
height: 24px;
left: 50%;
top: 50%;
margin: -12px 0 0 -12px;
transform: scale(1);
-webkit-transition: all 0.2s linear;
transition: all 0.2s linear;
}
.widget-callback .callback-button-phone:after {
content: '';
background: url(cancel.svg);
background-size: contain;
position: absolute;
display: block;
width: 24px;
height: 24px;
left: 50%;
top: 50%;
margin: -12px 0 0 -12px;
transform: scale(0);
-webkit-transition: all 0.2s linear;
transition: all 0.2s linear;
}
.widget-callback.widget-callback-form-open .callback-button-phone:before {
content: '';
transform: scale(0);
}
.widget-callback.widget-callback-form-open .callback-button-phone:after {
content: '';
transform: scale(1);
}
.widget-callback .callback-form {
display: none;
background: #fff;
border: 1px solid #f9f9f9;
width: 240px;
border-radius: 5px;
padding: 30px 15px;
right: 60px;
bottom: 110px;
position: absolute;
box-shadow: 0px 5px 20px rgba(0, 0, 0, 0.08);
}
.widget-callback.widget-callback-form-open .callback-form {
display: block;
animation: formcbwg-in 0.1s ease;
}
@keyframes formcbwg-in {
0% { transform: translateY(15%); }
100% { transform: translateY(0%); }
}
@keyframes formcbwg-out {
0% { transform: translateY(0%); }
100% { transform: translateY(15%); }
}
.widget-callback .callback-form-title {
text-align: center;
display: block;
margin: 0 0 30px;
}
.widget-callback input {
width: 100%;
box-sizing: border-box;
padding: 15px;
margin: 0 0 10px;
border: 1px solid #ebebeb;
border-radius: 3px;
font-size: 16px;
}
.widget-callback input[type="submit"] {
background: #199c68;
color: #fff;
text-transform: uppercase;
font-size: 14px;
border: none;
cursor: pointer;
}
.widget-callback .success-send {
text-align: center;
}
.widget-callback .success-send img {
width: 60px;
margin: 0 0 20px;
}
@media (max-width: 600px) {
.widget-callback .callback-button {
right: 30px;
bottom: 30px;
}
.widget-callback .callback-form {
width: 80%;
right: 10%;
box-sizing: border-box;
}
}
@keyframes shake {
0% {
transform: rotateZ(0deg);
-ms-transform: rotateZ(0deg);
-webkit-transform: rotateZ(0deg);
}
10% {
transform: rotateZ(-30deg);
-ms-transform: rotateZ(-30deg);
-webkit-transform: rotateZ(-30deg);
}
20% {
transform: rotateZ(15deg);
-ms-transform: rotateZ(15deg);
-webkit-transform: rotateZ(15deg);
}
30% {
transform: rotateZ(-10deg);
-ms-transform: rotateZ(-10deg);
-webkit-transform: rotateZ(-10deg);
}
40% {
transform: rotateZ(7.5deg);
-ms-transform: rotateZ(7.5deg);
-webkit-transform: rotateZ(7.5deg);
}
50% {
transform: rotateZ(-6deg);
-ms-transform: rotateZ(-6deg);
-webkit-transform: rotateZ(-6deg);
}
60% {
transform: rotateZ(5deg);
-ms-transform: rotateZ(5deg);
-webkit-transform: rotateZ(5deg);
}
70% {
transform: rotateZ(-4.28571deg);
-ms-transform: rotateZ(-4.28571deg);
-webkit-transform: rotateZ(-4.28571deg);
}
80% {
transform: rotateZ(3.75deg);
-ms-transform: rotateZ(3.75deg);
-webkit-transform: rotateZ(3.75deg);
}
90% {
transform: rotateZ(-3.33333deg);
-ms-transform: rotateZ(-3.33333deg);
-webkit-transform: rotateZ(-3.33333deg);
}
100% {
transform: rotateZ(0deg);
-ms-transform: rotateZ(0deg);
-webkit-transform: rotateZ(0deg);
}
}
@-webkit-keyframes shake {
0% {
transform: rotateZ(0deg);
-ms-transform: rotateZ(0deg);
-webkit-transform: rotateZ(0deg);
}
10% {
transform: rotateZ(-30deg);
-ms-transform: rotateZ(-30deg);
-webkit-transform: rotateZ(-30deg);
}
20% {
transform: rotateZ(15deg);
-ms-transform: rotateZ(15deg);
-webkit-transform: rotateZ(15deg);
}
30% {
transform: rotateZ(-10deg);
-ms-transform: rotateZ(-10deg);
-webkit-transform: rotateZ(-10deg);
}
40% {
transform: rotateZ(7.5deg);
-ms-transform: rotateZ(7.5deg);
-webkit-transform: rotateZ(7.5deg);
}
50% {
transform: rotateZ(-6deg);
-ms-transform: rotateZ(-6deg);
-webkit-transform: rotateZ(-6deg);
}
60% {
transform: rotateZ(5deg);
-ms-transform: rotateZ(5deg);
-webkit-transform: rotateZ(5deg);
}
70% {
transform: rotateZ(-4.28571deg);
-ms-transform: rotateZ(-4.28571deg);
-webkit-transform: rotateZ(-4.28571deg);
}
80% {
transform: rotateZ(3.75deg);
-ms-transform: rotateZ(3.75deg);
-webkit-transform: rotateZ(3.75deg);
}
90% {
transform: rotateZ(-3.33333deg);
-ms-transform: rotateZ(-3.33333deg);
-webkit-transform: rotateZ(-3.33333deg);
}
100% {
transform: rotateZ(0deg);
-ms-transform: rotateZ(0deg);
-webkit-transform: rotateZ(0deg);
}
}
Информирование по email
Разметка готова, теперь нужно настроить отправку уведомлений на почту, для этого подключим файл mail.php со следующим содержимым:
<?php
if ($_SERVER["REQUEST_METHOD"] == "POST") {
if (isset($_POST['wgphone'])) {$wgphone = $_POST['wgphone'];}
if (isset($_POST['wgdata'])) {$wgdata = $_POST['wgdata'];}
if (isset($_POST['wgpage'])) {$wgpage = $_POST['wgpage'];}
$to = "up-lite@ya.ru"; /*Укажите адрес, на который должно приходить письмо*/
$headers = 'MIME-Version: 1.0' . "rn";
$headers .= "Content-type: text/html; charset=utf-8 rn";
// дополнительные данные
$headers .= "From: Студия Аплайт <info@up-lite.ru>;rn"; // от кого
$subject = "$wgdata";
$message = "
<div style='background: #f8f8f8;padding: 20px; font-family:sans-serif;'>
<div style='width: 400px;margin: 0 auto;'>
<div style=' background: #1b3c56;border-radius: 10px 10px 0 0;padding: 30px 0;text-align: center;color: #fff;font-weight: 700;font-size: 20px;'>Заявка на обратный звонок
</div>
<div style='padding: 30px;border-radius: 0 0 10px 10px;background: #fff;'>
<b>Телефон:</b> <a href='tel:".$wgphone."'>".$wgphone." </a><br>
<b>Страница:</b> ".$wgdata."
</div>
</div>
</div>
";
$send = mail ($to, $subject, $message, $headers);
if ($send == 'true')
{
echo '
<div class="success-send">
<img src="tick.svg"> <br> Мы получили Вашу заявку и скоро с Вами свяжемся!
</div>';
}
else
{
echo 'Нам не удалось отправить заявку, попробуйте еще раз';
}
} else {
http_response_code(403);
echo "Попробуйте еще раз";
}
?>
Вам остается только заменить email на свой, и форма будет работать. Как видите, в теле письма мы сформировали HTML-разметку, поэтому письма будут приходить в красиво оформленном формате:
![]()
Это удобно для администратора — сразу настроенная ссылка на телефон дает возможность не копировать номер, а сразу при клике перейти в телефонную книгу.
AJAX-отправка данных формы
Теперь добавить AJAX-отправку данных формы. Это позволит отправлять номер телефона без перезагрузки, что очень удобно для посетителя.
Скрипт отправки формы:
<script>
$(document).ready(function () {
//ajax-отправка данных
jQuery("#calbback-widget-form").submit(function () {
var formID = jQuery(this).attr('id');
var formNm = jQuery('#' + formID);
jQuery.ajax({
type: "POST",
url: 'mail.php',
data: formNm.serialize(),
success: function (data) {
jQuery(formNm).html(data);
},
error: function (jqXHR, text, error) {
jQuery(formNm).html(error);
}
});
return false;
});
});
</script>
Скрипты можно объединить в один и подключить файлом – как удобно на вашем проекте.
Отправка СМС
Для отправки сообщений на свой номер или на номер клиента будем использовать сервис sms.ru. Регистрируемся и обращаем внимание на следующее:
1. API-ключ — на каждой странице он расположен в самом низу, копируем его.
2. Файл подключения к API sms.ru — переходим по ссылке (найти ее можно в разделе «Интеграции» -> «PHP» в первой табличке).
![]()
Файл sms.ru.php небходимо поместить в корневую папку с mail.php, а затем настроить подключение и отправку в самом файле mail.php. Выглядит это следующим образом:
//ОТПРАВКА СМС
require_once 'sms.ru.php';
$smsru = new SMSRU('Ваш api_id'); // Ваш уникальный программный ключ, который можно получить на главной странице
$data = new stdClass();
$data->to = 'Ваш номер или номер админа ;
$data->text = ''.$wgdata.' '.$wgphone.''; // Текст сообщения
$sms = $smsru->send_one($data); // Отправка сообщения и возврат данных в переменную
Преимущество этого сервиса в том, что можно отправлять до 5 СМС в день на свой номер бесплатно.
Проверяем отправку данных формы и видим, что теперь приходит и оповещение на номер телефона. Данные, которые приходят в сообщении, можно менять, но чем больше текста, тем дороже СМС.
Отправка данных в чат Telegram
Для отправки данных в мессенджер нам понадобится следующее:
- Создать бота.
- Создать чат, в который бот будет отправлять данные.
- Активировать бота.
- Проверить правильность настройки.
Создаем телеграм-бота.
В строке поиска ищем BotFather и даем ему команду /start, затем выбираем команду /newbot, следуем подсказкам и выбираем имя бота и как его будем вызывать. Выбранное имя может быть занято, вам сообщит об этом подсказка, поэтому придумайте уникальное имя — чтоб наверняка 🙂
После отправки имени BotFather отправит нам сообщение о том, что все готово, и пришлет специальный ключ — токен (см. скриншот), он понадобится для получения ID чата, в который будем отправлять данные заявки.
![]()
Отлично, бот готов. Создаем чат и добавляем в него бота.
- Слева в меню Телеграма выбираем «Создать группу».
- Пишем название группы, например «Заявки с сайта [название сайта]».
- Добавляем бота в созданную группу через поиск.
Затем нужно активировать бота командой /join @uplite_bot. Пишем ее в созданной группе, далее кликаем по имени бота @uplite_bot, и нас перебрасывает на диалог с нашим ботом, где его нужно активировать командой /start.
Теперь необходимо получить ID чата, делается это следующим запросом:
https://api.telegram.org/botXXXXXXXXXXXXXXXXXXXXXXX/getUpdates
Где XXXXXXXXXXXXXXXXXXXXXXX — токен вашего бота, полученный ранее.
Вставляем свой токен и переходим по ссылке, затем снова заходим в чат с нашим ботом и повторно активируем его командой /start, обновляем ссылку и ищем ID чата, он будет выглядеть следующим образом:
![]()
Ищем в тексте ID с черточкой — это и есть наш ID чата.
Итак, у нас теперь есть все данные для отправки заявки в чат Телеграма, осталось только настроить в mail.php.
//ОТПРАВКА В ТЕЛЕГРАМ
$token = "ваш_токен";
$chat_id = "id_чата";
$arr = array(
'Телефон: ' => $wgphone,
'Страница' => $wgpage,
'Тема' => $wgdata
);
foreach($arr as $key => $value) {
$txt .= "<b>".$key."</b> ".$value."%0A";
};
$sendToTelegram = fopen("https://api.telegram.org/bot{$token}/sendMessage?chat_id={$chat_id}&parse_mode=html&text={$txt}","r");
Проверяем – все должно работать 🙂 Теперь у нас есть виджет с различными вариантами отправки заявок, который удовлетворит пожелания большинства заказчиков.