Вариант модальных окон на JavaScript

В этой практической статье сделаем свою реализацию модальных окон с корректным отображением на любом экране

'1'

Результат можно увидеть в конце статьи



Прежде чем начать, хотел бы обратить внимание, что у блога есть телеграм канал https://t.me/frontips, где можно узнавать о выходе новых статей и со временем будет появляться больше интересной и полезной информации.

Поддержите развитие блога и канала подпиской!

А теперь перейдём к теме статьи ;) Приятного чтения!



Верстаем структуру

Сверстаем два модальных окна, которые изначально будут скрыты. Добавим модальным окнам data-атрибут data-modal, в котором укажем для каждого модального окна уникальное значение

Сделаем две кнопки, которые будут отвечать за открытие соответствующих модальных окон

Добавим немного изображений для фона

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <title>Modal</title>
    <link rel="stylesheet" href="css/bootstrap-reboot.min.css"/>
    <link rel="stylesheet" href="css/main.css"/>
  </head>
  <body>
  	<!-- Кнопки для открытия модальных окон -->
    <div class="buttons">
      <div class="buttons__flex">
        <div class="buttons__button buttons__button_one">Open Modal One</div>
        <div class="buttons__button buttons__button_two">Open Modal Two</div>
      </div>
    </div>

  	<!-- Изображения для фона -->
    <div class="images">
      <div class="images__image"><img src="https://picsum.photos/1920/1080" alt=""/></div>
      <div class="images__image"><img src="https://picsum.photos/1920/1081" alt=""/></div>
      <div class="images__image"><img src="https://picsum.photos/1920/1082" alt=""/></div>
      <div class="images__image"><img src="https://picsum.photos/1920/1083" alt=""/></div>
      <div class="images__image"><img src="https://picsum.photos/1920/1084" alt=""/></div>
      <div class="images__image"><img src="https://picsum.photos/1920/1085" alt=""/></div>
    </div>

  	<!-- Первое модальное окно -->
    <div class="modal" data-modal="one"> <!-- Основной блок модального окна -->
      <div class="modal__wrapper"> <!-- Обертка для центрирования окна с контентом -->
        <div class="modal__body"> <!-- Обертка для возможности прокрутки модального окна при недостаточной высоты окна браузера -->
          <div class="modal__inner"> <!-- Блок контентной области, который стилизуем -->
            <div class="modal__close">Close</div>
            <div class="modal__title">Modal One</div>
            <div class="modal__text">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quis ex, modi. Eos modi eum debitis vero impedit delectus cumque rem velit laboriosam sunt beatae odit, a cupiditate quae sit voluptatum.</div>
          </div>
        </div>
      </div>
    </div>

    <!-- Второе модальное окно -->
    <div class="modal" data-modal="two">
      <div class="modal__wrapper">
        <div class="modal__body">
          <div class="modal__inner">
            <div class="modal__close">Close</div>
            <div class="modal__title">Modal Two</div>
            <div class="modal__text">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Veniam, iure, corporis eum beatae ut deleniti aperiam placeat maxime, eveniet non error. Fugiat placeat sit voluptatibus cumque quis adipisci porro quod eligendi maxime nemo impedit omnis, deserunt, esse sequi quos ullam, eos voluptatem ipsa iure animi. Error soluta laborum excepturi fugiat ratione eum, provident omnis deleniti illum animi placeat ea esse sunt atque, exercitationem eveniet sed consectetur quia quod vitae nemo velit. Officiis, possimus quia fugit aut maiores ad dignissimos rerum mollitia vitae voluptates. Quae cum inventore voluptate voluptatem debitis harum dignissimos accusamus dolorem cumque atque quam, repudiandae nam, quasi, ipsam deserunt. Voluptatem doloribus repudiandae voluptatum asperiores, impedit culpa, excepturi maxime libero architecto repellat enim velit quasi, facere iusto magni fugiat, iste provident ducimus maiores. Quos dolorum, eveniet nihil blanditiis, sint placeat, velit dolor, iusto voluptatibus deleniti magni perspiciatis? Consequuntur nesciunt cupiditate, veritatis aliquam sed molestias maiores. Dolorem temporibus, deleniti ex ut quis asperiores, perspiciatis a, nemo placeat dicta possimus aperiam.</div>
          </div>
        </div>
      </div>
    </div>
    <script src="js/main.js"></script>
  </body>
</html>


Задаем стили

Если модальное окно не помещается в текущие размеры окна по высоте, добавим возможность прокрутки. Но чтобы полоса прокрутки не испортила внешний вид модального окна, скроем ее, используя следующие стили

.modal__body {
  overflow-y: auto;
  position: relative;
  margin: 0 24px;
  max-height: 100%;
  max-width: 380px;
  -ms-overflow-style: none; /* скрываем полосу прокрутки */
  scrollbar-width: none; /* скрываем полосу прокрутки */
}
.modal__body::-webkit-scrollbar { /* скрываем полосу прокрутки */
  display: none;
}


Стили с комментариями для модальных окон

html {
  -ms-overflow-style: none;
  scrollbar-width: none;
}
html::-webkit-scrollbar {
  display: none;
}
body {
  background: #121212;
}
.container {
  max-width: 1140px;
  margin: 0 auto;
}
.buttons {
  padding: 24px 0;
}
.buttons__flex {
  display: flex;
  justify-content: center;
  align-items: center;
}
.buttons__button {
  cursor: pointer;
  color: #fff;
  background: #3626a7;
  border-radius: 30px;
  height: 60px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 0 32px;
}
.buttons__button:not(:last-child) {
  margin-right: 24px;
}
.images {
  padding: 32px;
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-gap: 32px;
  gap: 32px;
}
.images__image {
  overflow: hidden;
  border-radius: 30px;
}
.images__image img {
  width: 100%;
  height: 100%;
  -o-object-fit: cover;
  object-fit: cover;
  display: block;
}
.modal { /* начальные стили */
  visibility: hidden; /* скрываем модальное окно */
  opacity: 0; /* делаем прозрачным */
  position: fixed; /* делаем блок фиксированным */
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  display: block;
  background: rgba(18,18,18,0.8); /* фон для затемнения */
  z-index: 9999; /* перекрываем все элементы */
  transition: visibility 0s 0.25s, opacity 0.25s; /* добавляем для плавного перехода */
}
.modal_active { /* активный класс модального окна */
  opacity: 1; /* убираем прозрачность */
  visibility: visible; /* отображаем блок модального окна */
  transition: visibility 0s 0s, opacity 0.25s; /* добавляем для плавного перехода */
}
.modal__wrapper { /* блок обертка для центрирования контентной части модального окна */
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
}
.modal__body { /* еще один блок обертка для возможности прокрутки контента, если модальное окно не помещается по высоты */
  overflow-y: auto; /* добавляем полосу прокрутки при недостаточной высоте */
  position: relative;
  margin: 0 24px; /* боковые отступы для мобильных экранов */
  max-height: 100%;
  max-width: 380px;
  -ms-overflow-style: none; /* скрываем полосу прокрутки */
  scrollbar-width: none; /* скрываем полосу прокрутки */
}
.modal__body::-webkit-scrollbar { /* скрываем полосу прокрутки */
  display: none;
}
.modal__inner { /* стилизуем контентную часть */
  display: flex;
  flex-direction: column;
  background: #3626a7;
  width: 100%;
  height: 100%;
  padding: 64px 24px;
  align-items: center;
  text-align: center;
  color: #fff;
  border-radius: 30px;
  position: relative;
  margin: 24px 0; /* отступы сверху и снизу, чтобы контентная часть модального окна не прилипала к границам браузера при недостаточной высоте */
}
.modal__close {
  position: absolute;
  top: 16px;
  right: 16px;
  cursor: pointer;
  font-size: 14px;
}
.modal__title {
  font-size: 24px;
  font-weight: 500;
  line-height: 1;
  margin-bottom: 24px;
}
.modal__text {
  font-weight: 500;
  font-size: 16px;
  line-height: 1.5;
}
@media (max-width: 575.98px) {
  .buttons__flex {
    flex-direction: column;
  }
  .buttons__button:not(:last-child) {
    margin-right: 0;
    margin-bottom: 24px;
  }
  .images {
    grid-template-columns: 1fr;
  }
}



Пишем логику на JavaScript

Логика простая - в функцию для открытия модального окна первым параметром передаем селектор кнопки, которая будет открывать модальное окно, вторым параметром - селектор соответствующего модального окна. При нажатии на кнопку, отображаем необходимое модальное окно, добавляя активный класс. При нажатии на кнопку закрытия или на затемнённую область вокруг модального окна - скрываем модальное окно, удаляя активный класс

Код с комментариями

const openModal = (triggerSelector, modalDataSelector) => { // объявляем функцию открытия модального окна, которая принимает в качестве параметров селекторы кнопки и соответствующего модального окна
  const trigger = document.querySelector(triggerSelector) // ищем кнопку по переданному селектору
  const modal = document.querySelector(modalDataSelector) // ищем модальное окно по переданному селектору
  if (!trigger || !modal) return // если такая кнопка или модальное окно не найдены, то прекращаем работу функции
  trigger.addEventListener('click', e => { // при клике на кнопку
    e.preventDefault() // предотвращаем браузерные действия (если кнопка сделана через тег ссылки <a href=""></a>, то отменяется переход по ссылке)
    modal.classList.add('modal_active') // отображаем модальное окно, добавив ему активный класс
  })
}
openModal('.buttons__button_one', '.modal[data-modal="one"]') // Запускаем функцию и передаем селекторы для первого модального окна
openModal('.buttons__button_two', '.modal[data-modal="two"]') // Запускаем функцию и передаем селекторы для второго модального окна

const closeModal = () => { // объявляем функцию закрытия модального окна
  const modals = document.querySelectorAll('.modal') // ищем все модальные окна
  if (!modals) return // если их нет, то прекращаем выполнение функции
  modals.forEach(el => { // если есть, то для каждого из них
    el.addEventListener('click', e => { // при клике
      if (e.target.closest('.modal__close')) { // если клик был клик на кнопке закрытия
        el.classList.remove('modal_active') // то скрываем модальное окно, удаляя активный класс
      }
      if (!e.target.closest('.modal__body')) { // если клик был за пределами контентной части модального окна, то есть на затемненную область
        el.classList.remove('modal_active') // то тоже скрываем модальное окно, удаляя активный класс
      }
    })
  })
}
closeModal() // вызываем функцию закрытия


Получаем следующий результат

Open Modal One
Open Modal Two


Итоги

Опять же, данная реализация не претендует на эталонность. Вы можете использовать данные модальные окна, как один из доступных вариантов. А также, поняв логику, можете написать свой вариант модальных окон, на основе данной реализации

Буду рад, если статья оказалась полезной

Спасибо за ваше внимание и уделённое время!



Друзья, стараюсь для вас, поддержите проект!

Подписывайтесь, впереди много всего интересного и полезного ;)

Telegram - https://t.me/frontips

VK - https://vk.com/frontips