Модальное окно для сайта на чистом CSS и JavaScript

В этой статье познакомимся со скриптом, предназначенным для динамического создания модальных окон на сайте. Разберём из чего он состоит, как его подключить и использовать.
Что такое модальное окно?
Модальное окно – это элемент интерфейса, которой визуально представляет собой «всплывающее окно», отображающееся над остальной частью страницы.
При этом показ окна обычно сопровождают затемнением всей другой части страницы. Это действие позволяет визуально отделить его от остального содержимого страницы, а также показать, что в данный момент только оно одно является активным элементом. При этом контент, расположенный под ним, делают недоступным (т.е. пользователь не сможет с ним взаимодействовать пока он не закроет это окно).
Вызов модального окна можно привязать к различным событиям на странице, но в большинстве сценариев это осуществляется при нажатии на кнопку или ссылку.
Изображение модального окна:

Оно состоит из заголовка (хедера), основной части и футера.
В заголовке обычно выводят название окна и элемент, с помощью которого его можно закрыть. В основной части располагают содержимое, а в футере – кнопки для выполнения различных действий.
Подключение CSS и JavaScript-файлов к странице
Исходные коды модального окна расположены на GitHub в рамках проекта ui-components
в папке modal
.
Для установки модального окна на страницу необходимо подключить к ней файлы:
<!-- Подключение CSS-файла -->
<link rel="stylesheet" href="modal.css">
<!-- Подключения JavaScript-файла -->
<script src="modal.js"></script>
Создание и настройка модального окна
Этот скрипт создаёт модальное окно динамически. То есть здесь не нужно вставлять какой-то HTML-код непосредственно на страницу. Реализовано это в коде через класс. Шаблон модального окна содержится в приватном свойстве #template
:
class ItcModal {
#template = '<div class="itc-modal-backdrop"><div class="itc-modal-content"><div class="itc-modal-header"><div class="itc-modal-title">{{title}}</div><span class="itc-modal-btn-close" title="Закрыть">×</span></div><div class="itc-modal-body">{{content}}</div>{{footer}}</div></div>';
// ...
}
Следовательно, для того, чтобы сделать модальное окно достаточно просто создать новый экземпляр класса ItcModal
:
const modal = new ItcModal();
При создании окна вы можете сразу же его настроить, для этого в ItcModal
необходимо передать аргумент в формате объекта:
const modal = new ItcModal({
title: 'Заголовок',
content: '<div>Содержимое модального окна...</div>',
footerButtons: [
{ class: 'btn btn-close', text: 'Закрыть', action: 'close' },
]
});
Ключ title
отвечает за заголовок, content
– за содержимое, footerButtons
– за кнопки в футере окна.
Эти ключи являются не обязательными. Если их не указать, то создастся окно с заголовком «Новое окно», без содержимого и кнопок:
const modal = new ItcModal();
Пример создания модального окна с заголовком «Какой-то текст» и содержимым <p>Мой контент</p>
:
const modal = new ItcModal({
title: 'Какой-то текст',
content: '<p>Мой контент</p>'
});
В качестве содержимого можно передавать HTML-код.
Добавление кнопок в футер окна осуществляется с помощью ключа footerButtons
. Он принимает в качестве значения массив объектов. Каждый объект в этом массиве представляет собой кнопку. Она в свою очередь задаётся посредством ключей text
, class
и action
. С помощью них вы можете кнопке соответственно установить текст, класс и атрибут data-action
:
const modal = new ItcModal({
title: '...',
content: '<div>...</div>',
footerButtons: [
{ class: 'btn btn-cancel', text: 'Отмена', action: 'cancel' },
{ class: 'btn btn-ok', text: 'ОК', action: 'ok' }
]
});
Методы
Управление созданным модальным окном осуществляется посредством методов:
show
– открытие;hide
– закрытие;dispose
– удаление из DOM HTML-элементов модального окна и обработчика событияclick
;setBody
– установка основного содержимого;setTitle
– изменение заголовка.
Открытие модального окна:
const modal = new ItcModal();
// открыть модальное окно
modal.show();
Скрытие модального окна:
modal.hide();
Изменение заголовка и тела модального окна:
// новый заголовок
modal.setTitle('Текст нового заголовка');
// новое тело
modal.setBody('<div>...</div>');
Уничтожение модального окна:
modal.dispose();
Данную операцию имеет смысл использовать только в том случае, если созданное окно вам больше не нужно на странице.
События
Если вам нужно выполнить какие-то действия при открытии и закрытии модального окна, то можете воспользоваться событиями:
// при открытии модального окна
document.addEventListener('show.itc.modal', (e) => {
// e.target - содержит ссылку на модальное окно
e.target.querySelector('.itc-modal-body').innerHTML = 'Содержимое модального окна, добавленное через <code>show.itc.modal</code>...';
});
// при закрытии модального окна
document.addEventListener('hide.itc.modal', (e) => {
// e.target - содержит ссылку на модальное окно
e.target.querySelector('.itc-modal-body').innerHTML = '';
});
Примеры
1. Открытие модального окна при нажатии на кнопку:
<button id="show-modal">Открыть</button>
<script>
// создаём модальное окно
const modal = new ItcModal();
// при клике по кнопке #show-modal
document.querySelector('#show-modal').addEventListener('click', () => {
// откроем модальное окно
modal.show();
});
</script>
2. Открытие одного и того же модального окна при нажатии на разные кнопки (определяется через data-атрибут data-toggle="modal"
):
<button data-toggle="modal">Кнопка 1</button>
<button data-toggle="modal">Кнопка 2</button>
<script>
const modal = new ItcModal({
content: 'Содержимое модального окна...'
});
// при клике на странице
document.addEventListener('click', (e) => {
if (e.target.closest('[data-toggle="modal"]')) {
modal.show();
}
});
</script>
3. Заголовок и содержимое модального окна устанавливается из значений data-атрибутов кнопки, посредством которой оно вызывается:
<button data-toggle="modal" data-title="Заголовок 1" data-content="Содержимое модального окна 1...">Кнопка 1</button>
<button data-toggle="modal" data-title="Заголовок 2" data-content="Содержимое модального окна 2...">Кнопка 2</button>
<script>
const modal = new ItcModal();
// при клике на странице
document.addEventListener('click', (e) => {
const btn = e.target.closest('[data-toggle="modal"]');
if (btn) {
modal.setTitle(btn.dataset.title);
modal.setBody(btn.dataset.content);
modal.show();
}
});
</script>
4. Обработка события click
для кнопок, расположенной в футере модального окна:
<div class="items">
<div class="item">
<img src="/examples/images/car-1.jpg" alt="" data-price="22500" data-name="Audi A5 Coupé">
</div>
...
</div>
<script>
const modal = new ItcModal({
title: 'Просмотр изображения',
content: '<img src="" alt="" style="display: block; height: auto; max-width: 100%;">',
footerButtons: [
{class: 'btn btn-delete', text: 'Удалить', action: 'delete'},
{class: 'btn btn-cancel', text: 'Закрыть', action: 'cancel'}
]
});
// при клике по документу
document.addEventListener('click', (e) => {
const img = e.target.closest('img');
// если мы кликнули на изображение, то...
if (img) {
img.classList.add('active');
// устанавливаем модальному окну title
modal.setBody(`<div style="flex: 1 0 50%;">
<img src="${img.src}" alt="${img.alt}" style="display: block; height: auto; max-width: 100%; margin: 0 auto;">
</div>
<div style="flex: 1 0 30%; text-align: center;">
<div style="font-size: 1.125rem; font-weight:bold;">
${img.dataset.name}
</div>
Цена:<br><b>${img.dataset.price}</b></div>`);
modal.show();
}
if (e.target.closest('[data-action="cancel"]')) {
modal.hide();
}
if (e.target.closest('[data-action="delete"]')) {
const img = document.querySelector('img.active');
img.parentElement.remove();
modal.hide();
}
});
</script>
5. Создание 2 разных модальных окон. Первое модальное окно открывается при нажатии на кнопки с data-атрибутом data-toggle="modal-1"
, а второе – при клике на data-toggle="modal-2"
:
<button data-toggle="modal-1">Открыть окно 1</button>
<button data-toggle="modal-1">Открыть окно 1</button>
<button data-toggle="modal-2">Открыть окно 2</button>
<button data-toggle="modal-2">Открыть окно 2</button>
<script>
// создадим модальное окно №1
const modal1 = new ItcModal({
title: 'Модальное окно 1',
content: 'Содержимое модального окна 1'
});
// создадим модальное окно №2
const modal2 = new ItcModal({
title: 'Модальное окно 2',
content: 'Содержимое модального окна 2'
});
document.addEventListener('click', (e) => {
// при клике по кнопке data-toggle="modal-1"
if (e.target.closest('[data-toggle="modal-1"]')) {
// откроем модальное окно №1
modal1.show();
}
// при клике по кнопке data-toggle="modal-2"
if (e.target.closest('[data-toggle="modal-2"]')) {
// откроем модальное окно №2
modal2.show();
}
});
</script>
6. Загрузка данных в модальное окно посредством AJAX:
<a href="#" data-json="/examples/pens/itc-modal/json-1">из json-1</a>
<a href="#" data-json="/examples/pens/itc-modal/json-2">из json-2</a>
...
<script>
// создадим модальное окно
const modal = new ItcModal({
title: 'Модальное окно',
});
// при клике по ссылке
document.addEventListener('click', function (e) {
const anchor = e.target.closest('a[data-json]');
if (anchor) {
e.preventDefault();
const request = new XMLHttpRequest();
request.open('GET', 'https://itchief.ru/' + e.target.dataset.json);
request.send();
request.onload = () => {
if (request.status === 200) {
const data = JSON.parse(request.response);
let html = '<div style="display: flex; gap: 1rem;"><div style="flex: 1 0 50%;"><img src="{{image}}" alt="" style="display: block; height: auto; max-width: 100%; margin: 0 auto;" width="705" height="440"></div><div style="flex: 1 0 30%; text-align: center;"><div style="font-size: 18px; font-weight:bold;">{{title}}</div>Цена:<br><b>{{price}}</b></div></div>';
html = html.replace('{{title}}', data.title);
html = html.replace('{{price}}', data.price);
html = html.replace('{{image}}', data.image);
modal.setBody(html);
// отобразим модальное окно
modal.show();
}
};
}
});
</script>
Пример JSON-файла:
{"title":"Audi A5 Coupé","price":"22500$","image":"https://itchief.ru/examples/images/car-1.jpg"}
7. Работа с событиями, возникающими при открытии и закрытии модального окна:
<!-- Кнопки для открытия модального окна -->
<button data-toggle="modal">Кнопка 1</button>
<button data-toggle="modal">Кнопка 2</button>
<div class="message"></div>
<script>
const modal = new ItcModal({
title: 'Текст заголовка',
content: '<p>Содержимое модального окна...</p>',
footerButtons: [
{class: 'btn btn-2', text: 'ОК', action: 'ok'},
{class: 'btn btn-1', text: 'Отмена', action: 'cancel'}
]
});
document.addEventListener('show.itc.modal', () => {
document.body.style.backgroundColor = '#fff59d';
});
document.addEventListener('hide.itc.modal', () => {
document.body.style.backgroundColor = '#fff';
});
document.addEventListener('click', (e) => {
document.querySelector('.message').innerHTML = '';
if (e.target.closest('[data-toggle="modal"]')) {
modal.setBody(`Вы открыли модальное окно при нажатии на <b>${e.target.textContent}</b>`);
modal.show();
}
if (e.target.closest('[data-action="cancel"]') || e.target.closest('[data-action="ok"]')) {
const text = (e.target.closest('[data-action="cancel"]') || e.target.closest('[data-action="ok"]')).textContent;
document.querySelector('.message').innerHTML = `Вы завершили действие с модальным окном посредством кнопки <b>${text}</b>`;
modal.hide();
}
if (e.target.closest('.itc-modal-btn-close')) {
document.querySelector('.message').textContent = 'Вы закрыли окно с помощью крестика';
}
});
</script>
Внутреннее устройство модального окна
Код JavaScript модального окна представлен посредством класса ItcModal
:
class ItcModal {
#elem;
#template = '<div class="itc-modal-backdrop"><div class="itc-modal-content"><div class="itc-modal-header"><div class="itc-modal-title">{{title}}</div><span class="itc-modal-btn-close" title="Закрыть">×</span></div><div class="itc-modal-body">{{content}}</div>{{footer}}</div></div>';
#templateFooter = '<div class="itc-modal-footer">{{buttons}}</div>';
#templateBtn = '<button type="button" class="{{class}}" data-action={{action}}>{{text}}</button>';
#eventShowModal = new Event('show.itc.modal');
#eventHideModal = new Event('hide.itc.modal');
#disposed = false;
constructor(options = []) {
// ...
}
#handlerCloseModal(e) {
// ...
}
show() {
// ...
}
hide() {
// ...
}
dispose() {
// ...
}
setBody(html) {
// ...
}
setTitle(text) {
// ...
}
};
В конструкторе мы создаём DOM-элемент и формируем его HTML-структуру. Ссылку на созданный элемент мы помещаем в приватное свойство #elem
. Данное свойство мы будем использовать в других методах ItcModal
. Для вставки на страницу модального окна используется метод append
.
Приватное свойство #disposed
применяется для хранения состояния. По умолчанию оно имеет значение false
. Это свойство связано с методом dispose()
. При вызове этого метода, модальное окно удаляется со страницы. Но, кроме этого, также удаляется событие, связанное с ним. Для отметки этого действия, свойству #dispose
присваивается значение true
:
dispose() {
this.#elem.remove(this.#elem);
this.#elem.removeEventListener('click', this.#handlerCloseModal);
this.#disposed = true;
}
После этого действия мы не сможем открыть модальное окно, так как его уже нет. Чтобы это не приводило к ошибкам в коде, в метод show
добавлена следующая проверка:
show() {
if (this.#disposed) {
// если модальное окно удалено, то завершаем работу
return;
}
this.#elem.classList.add('itc-modal-show');
this.#elem.dispatchEvent(this.#eventShowModal);
}
При открытии и закрытии модального окна, код генерирует события show.itc.modal
и hide.itc.modal
с помощью метода dispatchEvent
. Сами события находятся в приватных свойствах #eventShowModal
и #eventHideModal
. Эти события вызываются для this.#elem
. Используя их, вы можете очень просто добавить нужную логику при открытии и закрытии модального окна:
document.addEventListener('show.itc.modal', (e) => {
// e.target - содержит ссылку на модальное окно
// ...
});
document.addEventListener('hide.itc.modal', (e) => {
// e.target - содержит ссылку на модальное окно
// ...
});
Приватный метод #handlerCloseModal
является обработчиком события click
и используется для закрытия модального окна. То есть он выполняется при нажатии на крестик или при клике на backdrop
. Это действие мы вынесли в отдельный метод для того, чтобы потом мы могли удалить его при вызове метода dispose
.
Комментарии: 14
Добрый день. Спасибо за хорошее решение для вывода модального окна.
Подскажите. При вызове модального окна из области превышающей высоту видимости экрана пользователя, страница с контентом оказывается не в той области (срабатывает прокрутка к началу страницы), где было вызвано модальное окно, можно ли как-то это поправить?Добрый день! Внёс изменения в код на Github. Попробуйте сейчас.
Спасибо. Если применять 'button' то данная проблема отсутствует. Но хотелось бы вызвать модальное окно используя ссылку
Тогда в обработчике события нужно просто вызвать метод
e.preventDefault()
:Здравствуйте, Александр!
Очень понравился ваша технология модального окна.Всё работает прекрасно!
Хотел уточнить, мой PhpStorm в файле
modal.js
(8 строка) зачеркнул функциюevt.initCustomEvent
снабдив комментарием:Подскажите что в этой ситуации можно сделать?
Добрый день! Это было нужно для совместимости с IE11. Так как код компонента довольно устарел, переписал его с использованием классов. Код на GitHub обновил, а также переделал примеры в статье. Остальная часть статьи в процессе.
Здравствуйте, Александр!
Новый подход получился компактнее, изящнее и красивее!!! А значит и работать будет лучше. Респект и уважуха! :)У меня всего 1 вопрос:
Я использую ваше модальное окно для вызова формы обратной связи. При загрузке формы подключаются дополнительные обработчики событий (addEventListener), которые вступают в конфликт с обработчиком событий модального окна, который, в свою очередь, обрабатывается первым.Посоветуйте - в какую сторону копать? Делегирование событий?
Да, используйте делегирование. Почти все события всплывают. Вешайте обработчик на
document
или само модальное окно и там уже всё обрабатывайте.При использовании структуры Bootstrap 5 почему-то код не работает вовсе. Не могли бы вы подсказать как это исправить?
Добавил префиксы к классам. Сейчас не должно конфликтовать.
Здравствуйте Александр! Я использую первый пример модального окна. Возникла необходимость поместить в него форму входа на сайт. Но при добавлении события onclick в коде:
Окно не открывается. Код окна я добавляю через
modal.js
. Помогите пожалуйста решить проблему.Александр, добрый день. Подскажите, пожалуйста, нигде не могу найти подходящую информацию. Мне нужен код для нескольких модальных окон на основе jQuery. Объясняю. Есть 6 кнопок, 6 модальных окон с разным заголовком и текстом. При нажатии на первую кнопку должно появляться первое окно и закрываться при нажатии на крестик. При нажатии на вторую кнопку - второе окно и т.д. Подскажите, пожалуйста, где можно поискать что-то подобное? Заранее благодарна!
Привет!
Это можно реализовать самостоятельно.1) Придумать разметку модальному окну:
2) Написать стили: 3) Написать код на jQuery: Готовый пример: перейтиДобрый день, не могли мне подсказать по одному вопросу. У меня есть график, который рисует данные в реальном времени. Берёт он их с запроса ajax. Получается, что каждые три минуты он получает данные и рисует их. Получается волнообразная графика. Теперь мне нужно сделать алерт, чтоб он срабатывал, когда данные ниже определенного уровня. К примеру, данные всегда выше 600, но, если вдруг ниже 500 упадет, чтоб он сработал и вывел текст типа ошибка такая-то. И ещё чтоб можно было свернуть до того момента пока не решилась проблема, и в алерте не нажали исправлена. В простом встроенным алерте такое к сожалению нельзя, а сам затрудняюсь написать свой алерт, может кто то поможет(