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

Всплывающие сообщения для сайта на чистом 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, что для многих сайтов очень важно.

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

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

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

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

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

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

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

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

HTML
<!-- без заголовка -->
<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
/* 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. При нажатии на эту кнопку выполняется закрытие сообщения.

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

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

CSS
.toast_show {
  display: block;
}

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

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

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

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

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

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

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

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

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

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

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

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

sobolev
sobolev

Здравствуйте, а как сделать так чтобы после закрытия уведомления оно больше не показывалось для пользователя?

Александр Мальцев
Александр Мальцев

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

workpage
workpage

Добрый день.

Разобрался как вывести сообщение.

Не могу понять, как скрыть сообщение из JS, если не стоит autohide?

Пробую
toast-container.classList.remove("toast_show");
Не работает.
Александр Мальцев
Александр Мальцев

Добрый день! На GitHub имеется файл toast.dev.js. Можно подключить его вместо toast.js. В этом файле как раз имеется такая возможность:

Toast.hide(document.querySelector('.toast'))

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

MMOAGE
MMOAGE

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

Поставил в toastbody для обработки html – innerHTML.

Но вот проблема, уведомления у меня динамически, т.е. разные, выводятся в зависимости от типа и привязан вывод к id="error" (<div id="error"></div>). Проблема в том что уведомления когда исчезают то их «тело» просто скрывается, и получается наслоение <div id="error"></div>. Мой скрипт отправляет ошибку в первый блок <div id="error"></div>. А он, если уведомления уже были до этого, скрыт, и получается что у меня <div class="toastbody">div id="error"></div</div> пустой. А сообщение об ошибке в скрытом предыдущем уведомлении.

Вопрос: можно ли как-то делать reset уведомления или уничтожать их после сокрытия.
new Toast({
  title: false,
  text: 'Сообщение...',
  theme: 'light',
  autohide: true,
  interval: 10000
});
// Toast.destroy(); или Toast.reset(); - не работает
Александр Мальцев
Александр Мальцев

Добрый день! После скрытия элемента он удаляется из HTML для этого в коде используется метод remove().

WestDvina
WestDvina
Здравствуйте. А как реализовать оповещения на сайте в появляющиеся в определенное время. Например с 12 до 16 и в будние дни? Спасибо!
Александр Мальцев
Александр Мальцев
Добрый день! Можно реализовать так:
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
Если хотя бы одно из условий ложно, то оповещение не показываем.
WestDvina
WestDvina
Тогда еще один вопрос. text: 'Сообщение...' здесь html-теги допустимы? Например, вставка ссылки
WestDvina
WestDvina
Благодарю!
Anabolkick
Anabolkick
Добрый день, а можете подсказать, можно ли сделать, что б если сейчас активно больше какого-то количества сообщения, то что б при создании нового — самое старое исчезало?
Anabolkick
Anabolkick
Добрый день. Спасибо большое за статью. Хотел бы спросить, почему может возникать следущая проблема. Все работает до тех пор, пока не подключаю бутстрап <link rel=«stylesheet» href="~/lib/bootstrap/dist/css/bootstrap.min.css". После подключения бутстрапа окно всплывающего сообщения становиться прозрачным.
Anabolkick
Anabolkick
Как я понял, проблема в пересичении названий классов вашей библиотеки и бутстрапа. Хотел удалить комментарий, но не смог) Спасибо еще раз)
Raise_inc
Raise_inc
Привет. Спасибо за такое отличное окно, очень полезно). Только можешь сказать пожалуйста, как можно сделать плавное его появление и скрытие? Очень нужно( Я пробовал с opacity и visibility поиграть, в итоге все перестает работать. Заранее спасибо огромное! P.s. выше коммент не успел отредачить(
Александр Мальцев
Александр Мальцев
Привет! Спасибо за отзыв. Добавил плавное появление и скрытие.
pumble
pumble
Здравствуйте! Очень хороший пример, хоче внедрить его на свой сайт. Но к сожалению, у меня не получается. Если использую Ваш код в целости то все работает, но как только хочу оставить саму кнопку и удаляю лишнее, скрипт перестает работать (в таком виде):


              <!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>
Подскажите, пожалуйста, что я делаю не так и как внедрить это на своем сайте?
pumble
pumble
Вопрос закрыт :)
Sergby
Sergby
Привет. Очень круто все расписано и классные примеры. Но я так и не понял как заставить работать это на моем сайте.
Статьи расписаны легко и приятны для чтения. Спасибо Вам)
Александр Мальцев
Александр Мальцев
Привет! Просто подключаете «toast.min.css» и «toast.min.js» к странице. После этого в нужных местах своего скрипта, когда нужно вывести какую-то информацию пользователю (например, при добавлении товара в корзину), показываете всплывающее сообщение:
new Toast({
  title: false,
  text: 'Такой-то товар добавлен в корзину...',
  theme: 'light',
  autohide: false
});
Олег
Олег
Добрый день Александр! Пример очень хороший, именно такой искал для оповещений посетителям сайта. Единственное меня беспокоит о назойливостью, т.е. если посетитель будет переходить с одной странице сайта на другой то модальное окно будет постоянно появляться. Скажите пожалуйста, можно ли добавить временный таймер чтоб окно к примеру появлялось через 2 или 3 часа или может через 20 переходов страниц?
Александр Мальцев
Александр Мальцев
Привет! Для этого можно использовать 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 переходов, то аналогично.
Gio
Gio
Здравствуйте. Скажите, пожалуйста. Как я могу сделать так, чтобы уведомление получил отдельный пользователь? то есть: я создал интернет магазин и я хочу чтобы в браузере у заказчика выплыло уведомление о готовности товара и прочее. возможно ли это как то? заранее благодарю за ответ
Владимир
Владимир
Подскажите, пожалуйста, как сделать чтобы просто можно было прописать код всплывающего окна и оно всплывало когда открывается страница сайта?
Александр Мальцев
Александр Мальцев
Вот пример окна, появляющегося после загрузки страницы: modal-02
Александр Мальцев
Александр Мальцев
Добрый день!
Добавить звуковое оповещение можно так:
// создаем аудио объект
var snd = new Audio('toast.wav');
...
// добавляем проигрывание его в метод add
Toast.add = function (params) {
  ...
  snd.currentTime = 0;
  snd.play();
  return toast;
});
Пример со звуком можно посмотреть здесь.
Алексей
Алексей
Большое спасибо. А можете ещё подсказать, как добавить в тело сообщения ссылку
Александр Мальцев
Александр Мальцев
Пожалуйста. По умолчанию в тело сообщения можно вставлять только текстовый контент. Для того чтобы в тело можно было вставлять HTML элементы нужно заменить метод textContent на innerHTML:
toastBody.innerHTML = body;
Пример доступен по этой ссылке.
Алексей
Алексей
Александр, Здравствуйте.
А возможно к Toast добавить ещё и звуковое оповещение?