Привет друзья, в этому году меня несколько раз просили объяснить, как сделать модальное окно на сайте. Кто-то, изучая азы веб-разработки неизбежно приходил к ситуации, когда часть контент необходимо отобразить в модальном окне, кто-то, работая с формами, хочет показать ее не в открытом виде, а в 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" и повторяйте код, который описан в начале этой темы.
Если остались какие-то вопросы - пишите в комментариях. Попробуем разобраться.
14 комментариев
Все круто, но не смог разобраться что нужно добавить, чтобы для каждой кнопки открывалось свое модальное окно.
Денис, нужно дать каждому окну свой id, а потом при клике на кнопку добавлять 'modal_active' этому окну.
Дмитрий Давыдов, Можете на примере показать как это делается?
хочу несколько модальных окон сделать
т.к что то не понимаю куда это вставить
Андрей, так я же пример и скинул. В чем сложность? Как вы попробовали и у вас не получилось?
Такая ситуация: из скрипта вызываю модальное окно и как получить в скрипте из него значение после нажатия кнопки и его скрытия?
А как сделать такую же анимацию закрытия окна(улетает вверх). Как я понял оно делается через JS а в нём я ничего не понимаю.
Все круто очень работает, только вот один момент у меня не работает, получается что у меня изначально окно открыто, а как сделать так, что бы оно было скрыто, и появлялось только при клике?
Владимир, изначально задайте окну
, а в активном состоянии
Еще убедитесь, что сразу класс не задан у окна modal_active, а только modal
Не заметил этот момент,спасибо, часто читаю ваш блог и обучаюсь по нему, продолжайте в том же духе !!!
Здравствуйте. Как реализовать такое с Вашим методом?
1. На странице есть ссылка-кнопка Заказать звонок
2. При клике открывается модальное окно -> Заполняется -> Отправляется на почту.
3. При этом на странице имеется ТОЛЬКО вот эта ссылка-кнопка из п.1. Весь код формы лежит в файле на сервере. И вызывается при клике. Следовательно, вызов происходит через "#modal1".
Дима, подскажи, пожалуйста. При открывании модального окна экран слегка смещается вправо, так как скроллбар исчезает. Как убрать эти подергивания?
Игорь, Разобрался)
Спасибо!
Статья чудесная, благодаря ней Вы помогли мне сделать модальное окно! А как сделать так, чтобы оно открывалось при открытии страницы?