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

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

Если остались какие-то вопросы - пишите в комментариях. Попробуем разобраться.

Похожие публикации

3 комментария

  1. Денис

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

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

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

    $('#modal-1').addClass('modal_active');
  3. Kamikaze

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

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

 

© 2014 — 2021, Smartlanding.biz. Все права защищены. Любое использование материалов допускается только с указанием активной ссылки на источник. Политика конфиденциальности