Как сделать модальное окно на сайте

Привет друзья, в этому году меня несколько раз просили объяснить, как сделать модальное окно на сайте. Кто-то, изучая азы веб-разработки неизбежно приходил к ситуации, когда часть контент необходимо отобразить в модальном окне, кто-то, работая с формами, хочет показать ее не в открытом виде, а в 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 комментариев

  1. Денис

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

  2. Дмитрий Давыдов

    Денис, нужно дать каждому окну свой id, а потом при клике на кнопку добавлять 'modal_active' этому окну.

    $('#modal-1').addClass('modal_active');
    1. Андрей

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

    2. Дмитрий Давыдов

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

  3. Kamikaze

    Такая ситуация: из скрипта вызываю модальное окно и как получить в скрипте из него значение после нажатия кнопки и его скрытия?

  4. Egor

    А как сделать такую же анимацию закрытия окна(улетает вверх). Как я понял оно делается через JS а в нём я ничего не понимаю.

  5. Владимир

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

    1. Дмитрий Давыдов

      Владимир, изначально задайте окну

      visibility: hidden;

      , а в активном состоянии

      visibility: visible;

      Еще убедитесь, что сразу класс не задан у окна modal_active, а только modal

  6. Владимир

    Не заметил этот момент,спасибо, часто читаю ваш блог и обучаюсь по нему, продолжайте в том же духе !!!

  7. Надежда

    Здравствуйте. Как реализовать такое с Вашим методом?
    1. На странице есть ссылка-кнопка Заказать звонок
    2. При клике открывается модальное окно -> Заполняется -> Отправляется на почту.
    3. При этом на странице имеется ТОЛЬКО вот эта ссылка-кнопка из п.1. Весь код формы лежит в файле на сервере. И вызывается при клике. Следовательно, вызов происходит через "#modal1".

  8. Игорь

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

    1. Игорь

      Игорь, Разобрался)

  9. Aleks

    Спасибо!

  10. Michael

    Статья чудесная, благодаря ней Вы помогли мне сделать модальное окно! А как сделать так, чтобы оно открывалось при открытии страницы?

Добавить комментарий