Всплывающие сообщения для сайта на чистом JavaScript

Александр Мальцев
Александр Мальцев
34K
23
Всплывающие сообщения для сайта на чистом JavaScript
Содержание:
  1. Исходные коды
  2. Подключение и использование
  3. Подробное описание
  4. Комментарии

Статья, в которой рассмотрим, как можно самостоятельно создать для сайта всплывающие сообщения (уведомления) подобно тому, как это выполняет jGrowl (плагин для jQuery).

Исходные коды

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

Проект, рассматриваемый в рамках этой статьи, расположен на Github по адресу: https://github.com/itchief/ui-components/tree/master/toast

Он написан на чистом JavaScript без использования сторонних библиотек.

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

Всплывающие уведомления для сайта на чистом JavaScript

Демо

Подключение и использование

Компонент для показа всплывающих уведомлений состоит из 2 файлов: «toast.css» и «toast.js». Преимуществом данной библиотеки состоит в том, что она имеет очень маленький размер («toast.min.js» немного больше 1Кбайта). В отличие от библиотеки jGrowl эти сообщения не требуют библиотеку jQuery, что для многих сайтов очень важно.

Подключение компонента осуществляется посредством:

<link href="path/to/toast.min.css" rel="stylesheet">
<script src="path/to/toast.min.js"></script>

Вывод всплывающего сообщения на страницу осуществляется посредством создания экземпляра объекта Toast:

/*
  title - название заголовка
  text - текст сообщения
  theme - тема
  autohide - нужно ли автоматически скрыть всплывающее сообщение через interval миллисекунд
  interval - количество миллисекунд через которые необходимо скрыть сообщение
*/
new Toast({
  title: 'Заголовок',
  text: 'Сообщение...',
  theme: 'light',
  autohide: true,
  interval: 10000
});

Если нужно создать сообщение без заголовка, то нужно просто ключу title установить значение false:

// без заголовка
new Toast({
  title: false,
  text: 'Сообщение...',
  theme: 'light',
  autohide: true,
  interval: 10000
});

Подробное описание

Создание HTML кода всплывающих сообщений как с заголовком, так и без него выполняется в JavaScript. Целью является создание следующей структуры:

<!-- без заголовка -->
<div class="toast toast_message toast_default">
  <div class="toast__body">Сообщение...</div>
  <button class="toast__close" type="button"></button>
</div>

<!-- с заголовком -->
<div class="toast toast_default">
  <div class="toast__header">Заголовок</div>
  <div class="toast__body">Сообщение...</div>
  <button class="toast__close" type="button"></button>
</div>

HTML код сообщений простой. Он состоит из элемента с классом toast, в котором в зависимости от типа уведомления расположены два или три элемента:

  • <div> с классом toast__header - заголовок;
  • <div> с классом toast__body - элемент, в котором выводится само сообщение;
  • <button> с классом toast__close - кнопка, для закрытия сообщения.

С помощью классов в CSS добавляются стили к этим элементам:

/* CSS-переменные */
:root {
  --toast-border-radius: 0.25rem;
  --toast-theme-default: #fff;
}

.toast {
  font-size: 0.875rem;
  background-clip: padding-box;
  border: 1px solid rgba(0, 0, 0, 0.05);
  border-radius: var(--toast-border-radius);
  box-shadow: 0 .125rem .25rem rgba(0, 0, 0, 0.075);
  display: none;
  position: relative;
  overflow: hidden;
}

.toast_default {
  color: #212529;
  background-color: var(--toast-theme-default);
}

.toast:not(:last-child) {
  margin-bottom: 0.75rem;
}

.toast__header {
  position: relative;
  padding: 0.5rem 2.25rem 0.5rem 1rem;
  background-color: rgba(0, 0, 0, 0.03);
  border-bottom: 1px solid rgba(0, 0, 0, 0.05);
}

.toast__close {
  content: "";
  position: absolute;
  top: 0.75rem;
  right: 0.75rem;
  width: 0.875em;
  height: 0.875em;
  background: transparent url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/%3e%3c/svg%3e") center/0.875em auto no-repeat;
  border: 0;
  opacity: 0.5;
  cursor: pointer;
  transition: opacity 0.1s ease-in-out;
}

.toast__close:hover {
  opacity: 1;
}

.toast__body {
  padding: 1rem;
}

.toast_message .toast__body {
  padding-right: 2.25rem;
}

Класс toast__close ещё используется в обработчике события click. При нажатию на эту кнопку выполняется закрытие сообщения.

this._el.addEventListener('click', (e) => {
  if (e.target.classList.contains('toast__close')) {
    // вызываем метод, скрывающий сообщение
    this._hide();
  }
});

После того как JavaScript добавляет HTML код всплывающего сообщения на страницу, оно не отображается, т.к. по умолчанию оно имеет display: none. Его показ осуществляется после того, как к нему добавляется класс toast_show.

.toast_show {
  display: block;
}

Скрытие сообщения выполняется путём удаления класса toast_show.

Задание темы осуществляется посредством добавления класса.

Например, тема primary устанавливается так:

<div class="toast toast_primary">...</div>

Помещение элементов .toast выполняется в контейнер .toast-container. Его создание тоже осуществляется с помощью JavaScript, но только в том случае, если его нет на странице.

if (!document.querySelector('.toast-container')) {
  const container = document.createElement('div');
  container.classList.add('toast-container');
  document.body.append(container);
}

Данный элемент по умолчанию располагается в правом верхнем углу страницы и имеет ширину 270 пикселей. Если нужно расположить в другом месте, то измените стили.

:root {
  --toast-width: 270px;
}

.toast-container {
  position: fixed;
  top: 15px;
  right: 15px;
  width: var(--toast-width);
}

Написан код JavaScript в виде класса и имеет следующую структуру:

class Toast {
  constructor(params) { ... }
  _show() { ... }
  _hide() { ... }
  _create() { ... }
}

Комментарии: 23

  1. MMOAGE
    MMOAGE
    2022-11-23 10:27:09
    Добрый день, подскажите, поставил уведомления, подвязал к событию в скрипте, все работает показывает и тд.
    Поставил в toast__body для обработки html — innerHTML
    Но вот проблема, уведомления у меня динамически, т.е. разные, выводятся в зависимости от типа и привязан вывод к id=«error», div id=«error»> </div, проблема в том что уведомления когда исчезают то их «тело» просто скрывается, и получается наслоение div id=«error»> </div, мой скрипт отправляет ошибку в первый блок div id=«error»> </div, а он, если уведомления уже были до этого, скрыт, и получается что у меня div class=«toast__body»>div id=«error»> </div</div пустой, а сообщение об ошибку в скрытом предыдущем уведомлении.
    Вопрос: можно ли как то делать reset уведомления или уничтожать их после сокрытия.
    script >
    new Toast({
    title: false,
    text: 'Сообщение...',
    theme: 'light',
    autohide: true,
    interval: 10000
    });
    Toast.destroy(); или Toast.reset(); — не работает
    </ script

    извиняюсь за код, не знаю как его по другому вставить, удалил < что бы отобразился)
  1. WestDvina
    WestDvina
    2022-09-10 23:53:48
    Здравствуйте. А как реализовать оповещения на сайте в появляющиеся в определенное время. Например с 12 до 16 и в будние дни? Спасибо!
    1. Александр Мальцев
      Александр Мальцев
      2022-09-12 15:08:50
      Добрый день! Можно реализовать так:
      const now = new Date();
      const lastShowDate = window.localStorage.getItem('lastShowDate');
      const condition1 = lastShowDate !== now.toLocaleDateString();
      const condition2 = now.getDay() > 0 && now.getDay() < 6;
      const condition3 = now.getHours() > 12 && now.getHours() < 16;
      if (condition1 && condition2 && condition3) {
          window.setTimeout(() => {
            new Toast({
              title: 'Заголовок',
              text: 'Сообщение...',
              theme: 'dark',
              autohide: false
            });
            window.localStorage.setItem('lastShowDate', now.toLocaleDateString());
          }, 5000);
      }
      Здесь 3 условия:
      — если мы сегодня не показывали
      — если это будний день
      — если время больше 12 и меньше 16
      Если хотя бы одно из условий ложно, то оповещение не показываем.
    2. WestDvina
      WestDvina
      2022-09-12 15:32:25
      Тогда еще один вопрос. text: 'Сообщение...' здесь html-теги допустимы? Например, вставка ссылки
    3. WestDvina
      WestDvina
      2022-09-12 17:35:01
      Благодарю!
  2. Anabolkick
    Anabolkick
    2022-03-22 17:38:21
    Добрый день, а можете подсказать, можно ли сделать, что б если сейчас активно больше какого-то количества сообщения, то что б при создании нового — самое старое исчезало?
    1. Anabolkick
      Anabolkick
      2022-03-21 16:05:03
      Добрый день. Спасибо большое за статью. Хотел бы спросить, почему может возникать следущая проблема. Все работает до тех пор, пока не подключаю бутстрап <link rel=«stylesheet» href="~/lib/bootstrap/dist/css/bootstrap.min.css". После подключения бутстрапа окно всплывающего сообщения становиться прозрачным.
      1. Anabolkick
        Anabolkick
        2022-03-21 16:17:09
        Как я понял, проблема в пересичении названий классов вашей библиотеки и бутстрапа. Хотел удалить комментарий, но не смог) Спасибо еще раз)
    2. Raise_inc
      Raise_inc
      2022-03-19 00:09:35
      Привет. Спасибо за такое отличное окно, очень полезно). Только можешь сказать пожалуйста, как можно сделать плавное его появление и скрытие? Очень нужно( Я пробовал с opacity и visibility поиграть, в итоге все перестает работать. Заранее спасибо огромное! P.s. выше коммент не успел отредачить(
      1. Александр Мальцев
        Александр Мальцев
        2022-03-19 06:30:30
        Привет! Спасибо за отзыв. Добавил плавное появление и скрытие.
    3. pumble
      pumble
      2022-01-14 04:36:27
      Здравствуйте! Очень хороший пример, хоче внедрить его на свой сайт. Но к сожалению, у меня не получается. Если использую Ваш код в целости то все работает, но как только хочу оставить саму кнопку и удаляю лишнее, скрипт перестает работать (в таком виде):
      
      
                    <!DOCTYPE html>
      <html lang="ru">
      
      <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
        <title>Toast на чистом JavaScript - Пример 02</title>
        <style>
          body {
            margin: 0;
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
            font-size: 1rem;
            font-weight: 400;
            line-height: 1.5;
            color: #212529;
            text-align: left;
            background-color: #fff;
          }
      
          *,
          *::before,
          *::after {
            box-sizing: border-box;
          }
        </style>
        <link href="toast.min.css" rel="stylesheet">
        <script src="toast.min.js"></script>
      </head>
      
      <body style="min-height: 100vh;">
      
            <button id="add-toast" type="button" style="width: 100%;">Добавить toast</button>
          </div>
        </div>
      
        <script>
          document.querySelector('[name="toast-without-title"]').onchange = (e) => {
            document.querySelector('[name="toast-title"]').disabled = e.target.checked;
          }
      
          document.addEventListener('hide.toast', (e) => {
            console.log(e.detail.target);
          });
      
          document.querySelector('#add-toast').onclick = () => {
            /*
              title - название заголовка
              text - текст сообщения
              theme - тема
              autohide - нужно ли автоматически скрыть всплывающее сообщение через interval миллисекунд interval - количество миллисекунд через которые необходимо скрыть сообщение
            */
            new Toast({
        title: false,
        text: 'Gotowe...',
        theme: 'danger',
        autohide: true,
        interval: 10000
      });
          }
        </script>
      
      </body>
      
      </html>
      
      Подскажите, пожалуйста, что я делаю не так и как внедрить это на своем сайте?
      1. pumble
        pumble
        2022-01-14 14:24:30
        Вопрос закрыт :)
    4. Sergby
      Sergby
      2021-12-05 21:16:29
      Привет. Очень круто все расписано и классные примеры. Но я так и не понял как заставить работать это на моем сайте.
      Статьи расписаны легко и приятны для чтения. Спасибо Вам)
      1. Александр Мальцев
        Александр Мальцев
        2021-12-06 14:28:15
        Привет! Просто подключаете «toast.min.css» и «toast.min.js» к странице. После этого в нужных местах своего скрипта, когда нужно вывести какую-то информацию пользователю (например, при добавлении товара в корзину), показываете всплывающее сообщение:
        new Toast({
          title: false,
          text: 'Такой-то товар добавлен в корзину...',
          theme: 'light',
          autohide: false
        });
    5. Олег
      Олег
      2021-09-24 08:14:38
      Добрый день Александр! Пример очень хороший, именно такой искал для оповещений посетителям сайта. Единственное меня беспокоит о назойливостью, т.е. если посетитель будет переходить с одной странице сайта на другой то модальное окно будет постоянно появляться. Скажите пожалуйста, можно ли добавить временный таймер чтоб окно к примеру появлялось через 2 или 3 часа или может через 20 переходов страниц?
      1. Александр Мальцев
        Александр Мальцев
        2021-10-06 13:14:45
        Привет! Для этого можно использовать LocalStorage.
        window.addEventListener('load', (e) => {
          const showToast = () => {
            localStorage.setItem('toast-time', Date.now());
            new Toast({
              title: 'Заголовок',
              text: 'Сообщение...',
              theme: 'warning',
              autohide: false
            });
          }
          const time = localStorage.getItem('toast-time');
          if (time) {
            // если прошло больше 3 часов
            if (Date.now() > time + 1000 * 60 * 60 * 3) {
              showToast();
            }
          } else {
            showToast();
          }
        });
        В этом коде после загрузки получаем значение по ключу toast-time, содержащее время последнего показа сообщения. Если его нет, или прошло уже три часа, то показываем сообщение и записываем новое значение времени в LocalStorage. Если нужно через 20 переходов, то аналогично.
    6. Gio
      Gio
      2021-09-20 02:50:51
      Здравствуйте. Скажите, пожалуйста. Как я могу сделать так, чтобы уведомление получил отдельный пользователь? то есть: я создал интернет магазин и я хочу чтобы в браузере у заказчика выплыло уведомление о готовности товара и прочее. возможно ли это как то? заранее благодарю за ответ
      1. Владимир
        Владимир
        2021-06-24 18:52:34
        Подскажите, пожалуйста, как сделать чтобы просто можно было прописать код всплывающего окна и оно всплывало когда открывается страница сайта?
        1. Александр Мальцев
          Александр Мальцев
          2021-06-27 09:30:44
          Вот пример окна, появляющегося после загрузки страницы: modal-02
      2. Александр Мальцев
        Александр Мальцев
        2019-12-07 11:01:09
        Добрый день!
        Добавить звуковое оповещение можно так:
        // создаем аудио объект
        var snd = new Audio('toast.wav');
        ...
        // добавляем проигрывание его в метод add
        Toast.add = function (params) {
          ...
          snd.currentTime = 0;
          snd.play();
          return toast;
        });
        Пример со звуком можно посмотреть здесь.
        1. Алексей
          Алексей
          2019-12-07 11:09:34
          Большое спасибо. А можете ещё подсказать, как добавить в тело сообщения ссылку
        2. Александр Мальцев
          Александр Мальцев
          2019-12-07 11:35:25
          Пожалуйста. По умолчанию в тело сообщения можно вставлять только текстовый контент. Для того чтобы в тело можно было вставлять HTML элементы нужно заменить метод textContent на innerHTML:
          toastBody.innerHTML = body;
          
          Пример доступен по этой ссылке.
      3. Алексей
        Алексей
        2019-12-07 09:05:43
        Александр, Здравствуйте.
        А возможно к Toast добавить ещё и звуковое оповещение?