Привет друзья, в этому году меня несколько раз просили объяснить, как сделать модальное окно на сайте. Кто-то, изучая азы веб-разработки неизбежно приходил к ситуации, когда часть контент необходимо отобразить в модальном окне, кто-то, работая с формами, хочет показать ее не в открытом виде, а в pop-up при клике на кнопку.
Несмотря на то, что на блоге уже были статьи, косвенно связанные с реализацией этого эффекта, вопросы все равно остаются и я решил сделать несколько статей, связанных с этой темой. Сегодня разберемся просто с модальными окнами, а в следующий раз сделаем пульсирующую кнопку в углу экрана, как у сервисов обратного звонка и еще просили урок с анимацией блика на кнопке.
Все это выйдет друг за другом. Конечно, это статьи, ориентированные на новичков, и мастодонтам они будут не очень интересны, но тем, кто использует блог как справочник, возможно, пригодятся эти наработки, чтобы быстро реализовывать тот или иной эффект, не описывая одно и тоже каждый раз в новом проекте и сэкономят немного времени. Приступим.
Чтобы статья получилась максимально полной, будем придерживаться следующего плана и сделаем:
- модальное окно с использованием jQuery.
- Простое модальное окно с использованием чистого js.
Конечно же я покажу как вызывать несколько модальных окно на станице, так как это тоже один из частых вопросов, возникающих у новичков. Разберем возможность закрытия окна при клике вне области контента, решим проблему вертикальной прокрути и другие нюансы, которые возникнут в ходе работы или о которых сообщите вы в комментариях.
Структура проекта для понимания какие файлы и где находятся.

Начнем с разметки. Для создания модального окна я обычно пользуюсь такой структурой.
<div class="modal"> <div class="modal__content"> <!-- Контент модального окна --> </div> </div>
Где "modal" — это затемненный фон, а "modal__content" - область контента. При этом, когда срабатывает триггер, вызывающий окно, например нажатие кнопки, то к "modal" добавляется класс "modal_active", который дает понять, что модальное окно находится в открытом состоянии. Добавляя или удаляя "modal_active" мы будем открывать или скрывать модальное окно манипулирую стилями.
Большинству читающих нужна будет форма в модальном окне. Можно использовать эту.
Я, для примера, просто выведу текст:
<!-- Модальное окно --> <div class="modal"> <div class="modal__content"> <button class="modal__close-button"><img src="./img/close.svg" width="18" alt=""></button> <!-- Контент модального окна --> <h1 class="modal__title">Контент для примера</h1> <p class="modal__descruption">Статья с уроком - <a href="/kak-sdelat-modalnoe-okno-na-sajte.html">тут</a>.</p> </div> </div>
Общая картина:
<header class="header"> <div class="header__inner"> <div class="header__logo"> <p>Smartlanding</p> </div> <button id="callback-button" class="header__button">Обратный звонок</button> </div> </header> <!-- Модальное окно --> <div class="modal"> <div class="modal__content"> <button class="modal__close-button"><img src="./img/close.svg" width="18" alt=""></button> <!-- Контент модального окна --> <h1 class="modal__title">Контент для примера</h1> <p class="modal__description">Статья с уроком - <a href="/kak-sdelat-modalnoe-okno-na-sajte.html">тут</a>.</p> </div> </div>
Создал простейшую шапку сайта с кнопкой обратного звонка, которая должна вызвать модальное окно. Классический пример любого лендинга.

Давайте добавим немного стилей и приведем в порядок открытое состояние модалки. Вот, что получилось у меня.

Обратите внимание, что в правом углу контентной области модального окна появилась кнопка с изображением крестика. Файл лежит в папке "img", рядом с папкой "js" и "css".
.modal { position: fixed; top: 0; right: 0; bottom: 0; left: 0; z-index: 1000; background: rgba(0, 0, 0, 0.9); display: flex; justify-content: center; align-items: flex-start; overflow-y: scroll; padding: 60px 15px; } .modal__content { width: 100%; max-width: 500px; padding: 50px; background: #f9f9f9; border-radius: 3px; position: relative; box-shadow: 0 5px 15px black; } .modal__close-button { background: #ff0000; height: 30px; width: 30px; display: flex; justify-content: center; align-items: center; border: none; position: absolute; right: 0; top: 0; background: none; cursor: pointer; transition: .3s; outline: none; } .modal__close-button:hover { transition: .3s; transform: rotate(180deg); } .modal__title { font-size: 1.8rem; text-transform: uppercase; margin: 0 0 15px; } .modal__description { font-size: 1.125rem; }
Тут нас больше всего интересует класс ".modal". Как я и говорил, это само модальное окно с полупрозрачным фоном. Давайте подробнее разберем что там за стили.
Первым делом "вырвем" из потока блок с модальным окном при помощи фиксированного позиционирования. Затем, при помощи свойств "top", "left", "right", "bottom" растянем модальное окно по ширине на весь экран. Z-index - нужен для того, чтобы расположить окно над всеми элементами. Если у кого-то другого элемента значение этого свойства будет выше, то модальное окно не перекроет его.
position: fixed; top: 0; right: 0; bottom: 0; left: 0; z-index: 1000;
Далее задаю цвет фона модального окна.
background: rgba(0, 0, 0, .9);
Цвет задаю в RGBA формате. Первые 3 цифры — это цвет, а последняя — прозрачность. Где ".9" — это прозрачность в 90%.
Далее использую flexbox для центрирования элемента "modal__content" внутри "modal", задаю возможность вертикальной прокрутки и внутренние отступы.
display: flex; justify-content: center; align-items: flex-start; overflow-y: scroll; padding: 60px 15px;
Окно прекрасно смотрится, но есть одно "но!". Помните я говорил, что активное состояния модального окна, это когда у нас есть класс "modal_active", а без этого класса, окно закрыто? Сейчас мы доведем до ума эту концепцию. Суть в том, чтобы при "modal" сделать окно скрытым, а при "modal_active" - показать.
Давайте создадим этот класс в стилях и настроим отображение окна только при его наличии.
Для показа/скрытия окна будем пользоваться "visibility", "opacity", "position" и свойство "transition" для плавности анимации появления.
Если еще подробнее, то сначала зададим нулевую прозрачность, а при помощи абсолютного позиционирования и свойства "top" уберем модальное окно за пределы видимости. Дополнительно, для надежности, при помощи свойства "visibility" скроем разметку окна.
В момент, когда появится класс "modal_active" инвертируем "visibility" и "opacity", а также поменять "position" и положение в свойстве "top".
Звучит страшнее, чем кажется, на сам деле все довольно просто.
.modal { position: absolute; visibility: hidden; opacity: 0; transition: .3s; top: -1000px; right: 0; bottom: 0; left: 0; z-index: 1000; background: rgba(0, 0, 0, 0.9); display: flex; justify-content: center; align-items: flex-start; overflow-y: scroll; padding: 60px 15px; } .modal_active { position: fixed; top: 0; visibility: visible; opacity: 1; transition: .3s; }
Теперь в таком состоянии окно скрыто:
<div class="modal">
А в таком, окно открыто:
<div class="modal modal_active">
Попробуйте добавить вручную и убедиться, что окно успешно появляется.
Пол дела сделали, осталось добавлять этот класс модальному окну при клике по кнопке. Тут подход и разделится. Первый вариант сделаем с использованием jQuery, а второй без него на чистом js.
Но прежде нужно подключить файл скриптов к нашей html страничке. Делается это так:
<body> <header class="header"> <div class="header__inner"> <div class="header__logo"> <p>Smartlanding</p> </div> <div class="header__buttons"> <button id="callback-button" class="header__button">Обратный звонок</button> </div> </div> </header> <!-- Модальное окно --> <div class="modal" id="modal-1"> <div class="modal__content"> <button class="modal__close-button"><img src="./img/close.svg" width="12" alt=""></button> <!-- Контент модального окна --> <h1 class="modal__title">Контент для примера</h1> <p class="modal__description">Статья с уроком - <a href="/kak-sdelat-modalnoe-okno-na-sajte.html">тут</a>.</p> </div> </div> <script src="./js/scripts.js"></script> </body>
Вызов модального окна при помощи jQuery
Исходник модального окна с использованием jQuery
Размер: 0,004 мб
Первым делом нужно подключить сам jQuery, перед скриптом, который мы добавили чуть выше. То есть порядок подключения должен быть таким:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script> <script src="./js/scripts.js"></script>
Как и условились ранее, вызов окна будет происходить при клике на кнопку в шапке. Давайте обработаем это событие, предварительно объявив в файле "scripts.js", что весь код написанный в нем должен исполняться после загрузки страницы.
$(function () { $('#callback-button').click(function () { console.log('Клик!'); }) });
'#callback-button' - id кнопки, по которой кликаем. Если все сделали правильно, в консоли браузера должны увидеть строку с текстом "Клик!".

Инструменты разработчика можно открыть командой ctrl+shift+i, в некоторых браузерах клавишей f12 или через меню.
Теперь давайте добавим класс "modal_active" нашему модальному окну, а при клике на кнопку с крестиком удалим его.
$(function () { $('#callback-button').click(function () { $('.modal').addClass('modal_active'); }); $('.modal__close-button').click(function () { $('.modal').removeClass('modal_active'); }); });
Попробуйте, смотрится совсем не плохо.
Теперь давайте реализуем функционал, когда модальное окно закрывается при клике, вне области контента этого окна. Делается это довольно просто.
// Закрытие модального окна при клике вне его контентной области $('.modal').mouseup(function (e) { let modalContent = $(".modal__content"); if (!modalContent.is(e.target) && modalContent.has(e.target).length === 0) { $(this).removeClass('modal_active'); } });
Проверяйте, все должно отлично работать.
Теперь давайте разберем небольшой нюанс. Когда у вас страница наполнится контентом, то неизбежно появится вертикальная прокрутка.

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

Делается это довольно просто. При вызове окна мы просто уберем у тега "body" возможность прокрути. А когда окно будет закрываться, вернем все все на место.
Можно пойти 2 путями: менять стили прямо в js или создать специальный класс для тега body, который и будем убирать прокрутку. Я, обычно, пользуюсь вторым способом.
Давайте в таблице создадим класс ".hidden" и пропишем ему "overflow", запрещающий прокрутку.
.hidden { overflow: hidden; }
В скрипте, при открытии модального окна, запретим прокрутку у тега "body" путем добавления класса "hidden".
$('body').addClass('hidden');
При этом, не забывайте удалять его при закрытии модального окна. В целом все это выглядит так.
$(function () { $('#callback-button').click(function () { $('.modal').addClass('modal_active'); $('body').addClass('hidden'); }); $('.modal__close-button').click(function () { $('.modal').removeClass('modal_active'); $('body').removeClass('hidden'); }); $('.modal').mouseup(function (e) { let modalContent = $(".modal__content"); if (!modalContent.is(e.target) && modalContent.has(e.target).length === 0) { $(this).removeClass('modal_active'); $('body').removeClass('hidden'); } }); });
Если у вас разные кнопки вызывают разные модальные окна, то привязывайтесь не к классу окна, а к "id", который должен быть уникальным на странице. То есть если у модального окна id="modal-1", то и обращаться к нему можно так:
$('#modal-1').addClass('modal_active');
Если несколько кнопок вызывают одно и тоже окно, то можно перечислить их айди и классы так:
$('.callback-button, .consult-button').click(function (e) {
На этом способ реализации открытия модального окна при помощи jQuery можно считать оконченным. Если будут вопросы, то задавайте в комментариях - не стесняйтесь.
Открытие модального окна на чистом js
Исходник модального окна использованием чистого js
Размер: 0,004 мб
Тут дела обстоят немного сложнее, но если у вас в проекте не используется jQuery, то модальные окна точно не повод для его подключения, как минимум, на лендинге уж точно.
Как и в ситуации выше в файле "scripts.js" объявим, что скрипт должен выполняться после загрузки страницы и опишем реакцию на клик по кнопке.
Сначала разберем простейший пример, когда 1 кнопка вызывает конкретное модальное окно, а потом будем усложнять.
document.addEventListener('DOMContentLoaded', () => { // Кнопка по которой происходит клик let callBackButton = document.getElementById('callback-button'); // Модальное окно, которое необходимо открыть let modal1 = document.getElementById('modal-1'); // Кнопка "закрыть" внутри модального окна let closeButton = modal1.getElementsByClassName('modal__close-button')[0]; callBackButton.onclick = function () { modal1.classList.add('modal_active'); } closeButton.onclick = function () { modal1.classList.remove('modal_active'); } });
Итак, получили классы и "id" необходимых для работы элементов, а именно, "id" кнопки, по которой будет происходить клик, "id" модального окна, которое необходимо открыть и класс кнопки внутри модального окна, которое отвечает за его закрытие.
Ниже написал 2 простецкие функции, которые и отвечают за добавление и удаление класса "modal_active", при клике на соответствующие кнопки.
Попробуйте, все должно работать. Если нет, откройте консоль браузера и изучите появившиеся там ошибки.
Как открыть инструменты разработчика в браузере писал выше.
Теперь давайте сделаем так, чтобы наше модальное окно закрывалось при клике вне его контентной области. Для этого добавим следующую функцию:
modal1.onmousedown = function (e) { let target = e.target; let modalContent = modal1.getElementsByClassName('modal__content')[0]; if (e.target.closest('.' + modalContent.className) === null) { this.classList.remove('modal_active'); } };
Избавимся от двойной прокрутки при слишком длинной контентной области.
Описывал подробно со скринами эту ситуацию в примере с jQuery.
Создадим в "style.css" класс ".hidden", который будет запрещать прокрутку и будем добавлять его тегу "body", когда модальное окно открыто.
.hidden { overflow: hidden; }
В файле "scripts.js", там, где задавались основные переменные с элементами для взаимодействия, добавим еще одну в которую поместим тег "body".
let tagBody = document.getElementsByTagName('body');
В функции добавим код, который будет добавлять/удалять класс тегу "body". Пример всего кода:
document.addEventListener('DOMContentLoaded', () => { // Кнопка по которой происходит клик let callBackButton = document.getElementById('callback-button'); // Модальное окно, которое необходимо открыть let modal1 = document.getElementById('modal-1'); // Кнопка "закрыть" внутри модального окна let closeButton = modal1.getElementsByClassName('modal__close-button')[0]; // Тег body для запрета прокрутки let tagBody = document.getElementsByTagName('body'); callBackButton.onclick = function (e) { e.preventDefault(); modal1.classList.add('modal_active'); tagBody.classList.add('hidden'); } closeButton.onclick = function (e) { e.preventDefault(); modal1.classList.remove('modal_active'); tagBody.classList.remove('hidden'); } modal1.onmousedown = function (e) { let target = e.target; let modalContent = modal1.getElementsByClassName('modal__content')[0]; if (e.target.closest('.' + modalContent.className) === null) { this.classList.remove('modal_active'); tagBody.classList.remove('hidden'); } }; });
Реализуем возможность открытие модального окна несколькими кнопками.
Тут дела обстоят немного сложнее чем в jQuery и нужно будет перебрать все кнопки, которые должны вызывать одно и тоже модальное окно. Есть несколько подходов к реализации этой задачи.
Я, обычно, просто задаю дополнительный класс для кнопок, которые вызывают одно и тоже окно и в скрипте "цепляюсь" к нему.
Например, создам в шапке еще одну кнопку, дадим обеим кнопка общий класс и сделаем так, чтобы обе они вызывали наше модальное окно.
<div class="header__buttons"> <button id="callback-button-2" class="header__button get-modal_1">Консультация</button> <button id="callback-button" class="header__button get-modal_1">Обратный звонок</button> </div>
В файле скриптов опишем новую функцию, которая будет "проходиться" по всем элементам с классом "get-modal_1" и при клике на них вызывать модальное окно.
// Вызов модального окна несколькими кнопками на странице let buttonOpenModal1 = document.getElementsByClassName('get-modal_1'); for (let button of buttonOpenModal1) { button.onclick = function (e) { e.preventDefault(); modal1.classList.add('modal_active'); tagBody.classList.add('hidden'); } }
Как видите, все почти тоже самое, просто используем цикл.
Описать это все было сложнее чем сделать, на само деле финальный вариант выглядит так:
document.addEventListener('DOMContentLoaded', () => { // Кнопка по которой происходит клик let callBackButton = document.getElementById('callback-button'); // Модальное окно, которое необходимо открыть let modal1 = document.getElementById('modal-1'); // Кнопка "закрыть" внутри модального окна let closeButton = modal1.getElementsByClassName('modal__close-button')[0]; // Тег body для запрета прокрутки let tagBody = document.getElementsByTagName('body'); callBackButton.onclick = function (e) { e.preventDefault(); modal1.classList.add('modal_active'); tagBody.classList.add('hidden'); } closeButton.onclick = function (e) { e.preventDefault(); modal1.classList.remove('modal_active'); tagBody.classList.remove('hidden'); } modal1.onmousedown = function (e) { let target = e.target; let modalContent = modal1.getElementsByClassName('modal__content')[0]; if (e.target.closest('.' + modalContent.className) === null) { this.classList.remove('modal_active'); tagBody.classList.remove('hidden'); } }; // Вызов модального окна несколькими кнопками на странице let buttonOpenModal1 = document.getElementsByClassName('get-modal_1'); for (let button of buttonOpenModal1) { button.onclick = function (e) { e.preventDefault(); modal1.classList.add('modal_active'); tagBody.classList.add('hidden'); } } });
Когда нужно несколько модальных окон, просто задавайте разный "id" и повторяйте код, который описан в начале этой темы.
Если остались какие-то вопросы - пишите в комментариях. Попробуем разобраться.
2 комментария
Все круто, но не смог разобраться что нужно добавить, чтобы для каждой кнопки открывалось свое модальное окно.
Денис, нужно дать каждому окну свой id, а потом при клике на кнопку добавлять 'modal_active' этому окну.