Модальные окна на javascript. 30 строк кода

В этой статье мы разберемся как создавать модальные окна с помощью javascript.

Модальные окна которые мы сделаем буду универсальны. Для создания модальных окон в дальнейшем вам не нужно будет дописывать что-то в .js файле.

Демо на codepen (Javascript)
Демо на я codepen (jQuery)

Создадим разметку:

<!-- Элементы для вызова модальных окон, могут быть любые -->

<a href="#">Открыть окно 1</a>
<a href="#">Открыть окно 2</a>


<!-- Несколько модальных окон -->

<div class="modal" data-modal="1">
   <!--   Svg иконка для закрытия окна  -->
   <svg class="modal__cross" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M23.954 21.03l-9.184-9.095 9.092-9.174-2.832-2.807-9.09 9.179-9.176-9.088-2.81 2.81 9.186 9.105-9.095 9.184 2.81 2.81 9.112-9.192 9.18 9.1z"/></svg>
   <p class="modal__title">Заголовок окна 1</p>
</div>

<div class="modal" data-modal="2">
   <!--   Svg иконка для закрытия окна  -->
   <svg class="modal__cross" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M23.954 21.03l-9.184-9.095 9.092-9.174-2.832-2.807-9.09 9.179-9.176-9.088-2.81 2.81 9.186 9.105-9.095 9.184 2.81 2.81 9.112-9.192 9.18 9.1z"/></svg>
   <p class="modal__title">Заголовок окна 2</p>
</div>

<!-- Подложка под модальным окном -->
<div class="overlay" id="overlay-modal"></div>

Css код:

/* Стили для подложки */

.overlay {
   
   /* Скрываем подложку  */
   opacity: 0;
   visibility: hidden;
   
   position: fixed;
   top: 0;
   left: 0;
   width: 100%;
   height: 100%;
   background-color: rgba(0, 0, 0, .5);
   z-index: 20;
   transition: .3s all;
}


/* Стили для модальных окон */

.modal {
   
   /* Скрываем окна  */
   opacity: 0;
   visibility: hidden;
   
   
   /*  Установаем ширину окна  */
   width: 100%;
   max-width: 500px;
   
   /*  Центрируем и задаем z-index */
   position: fixed;
   top: 50%;
   left: 50%;
   transform: translate(-50%, -50%);
   z-index: 30; /* Должен быть выше чем у подложки*/
   
   /*  Побочные стили   */
   box-shadow: 0 3px 10px -.5px rgba(0, 0, 0, .2); 
   text-align: center;
   padding: 30px;
   border-radius: 3px;
   background-color: #fff;
   transition: 0.3s all;
}

/* Стили для кнопки закрытия */

.modal__cross {
   width: 15px;
   height: 15px;
   position: absolute;
   top: 20px;
   right: 20px;
   fill: #444;
   cursor: pointer;
}


/* Стили для кнопок. Мы ведь порядочные разработчики, негоже простые ссылки оставлять */

a {
   padding: 20px;
   display: inline-block;
   text-decoration: none;
   background-color: #414b52;
   margin: 10px;
   color: #fff;
   border-radius: 3px;
}

Вот что получилось в итоге (это скрин, не кликать :)):

Кнопки открытия окна

План работы скрипта:

  1. Зарегистрировать событие клика на элементы с классом js-open-modal
  2. При клике на кнопку, ищем модальное окно с таким же атрибутом data-modal, добавляем класс .active подложке и этому модальному окну
  3. При клике на крестик удаляем класс у родительского модального окна и подложки

Пишем JavaScript

В начале, повесим на document событие DOMContentLoaded. Это событие сработает когда страница будет загружена.

document.addEventListener('DOMContentLoaded', function() {

});

Затем запишем массив кнопок в переменную используя метод querySelector. Здесь же определим еще 2 переменные: элемент подложки и массив кнопок-крестиков.

document.addEventListener('DOMContentLoaded', function() {

   var modalButtons = document.querySelectorAll('.js-open-modal'),
       overlay      = document.querySelector('#overlay-modal'),
       closeButtons = document.querySelectorAll('.js-modal-close');
});

Заметьте, modalButtons и CloseButtons мы получаем через querySelectorAll, который возвращает массив, мы сделали это потому что нужно обрабатывать клики по всем кнопка, а вот overlay мы получаем через querySelector, он возвращает один элемент.

В html добавим кнопкам классы .js-open-modal. Мы специально будем использовать новый класс с приставкой js, чтобы не путать стили и интерактивность. Все кто будет работать с кодом увидит, что у класса есть приставка js, значит этот класс используется для интерактивности и его лучше не трогать.

<!-- Элементы для вызова модальных окон, могут быть любые -->

<a href="#" class="js-open-modal">Открыть окно 1</a>
<a href="#" class="js-open-modal">Открыть окно 2</a>

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

/* Перебираем массив кнопок */
modalButtons.forEach(function(item){

});

В переменной item у нас будет храниться текущий элемент цикла. Повесим обработчик на него:

/* Перебираем массив кнопок */
modalButtons.forEach(function(item){

    /* Назначаем каждой кнопке обработчик клика */
    item.addEventListener('click', function(event) {

     });
});

event или e — объект текущего события. В этом объекте хранятся различные методы и данные. При вызове любого события указание аргумента у функции будет ссылаться на этот объект. Зачем нам нужен этот объект? Об этом чуть ниже.

Что нам нужно сделать теперь?

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

Для этого в объекте события есть метод, который предотвращает стандартное действие элемента.

event.preventDefault();

С предотвращением вопрос решили.

У каждой кнопки есть атрибут data-modal, в нем хранится значение, которое находится у модального окна в таком же атрибуте. Наши действия:

  1. Получить значение атрибута текущей кнопки
  2. Найти модальное окно с помощью этого значения
var modalId = this.getAttribute('data-modal'),
    modalElem = document.querySelector('.modal[data-modal="' + modalId + '"]');

Для того чтобы найти модальное окно, мы пишем определенный селектор, а там где нужно вставить значение, делаем разрыв строки и вставляем значение переменной, соединяя строки используя оператор сложения, в случае со строками, он выполняет их соединение (конкатенацию).

В итоге получается такой селектор — ‘.modal=[data-modal="значение переменной"]’ , который и находит наше модальное окно.

Давайте добавим нашему окну и подложке класс active.

modalElem.classList.add('active');
overlay.classList.add('active');

Напишем стили для классов .active:

.modal.active,
.overlay.active{
   opacity: 1;
   visibility: visible;
}

Весь javascript код который получился:

document.addEventListener('DOMContentLoaded', function() {

   var modalButtons = document.querySelectorAll('.js-open-modal'),
       overlay      = document.querySelector('#overlay-modal'),
       closeButtons = document.querySelector('.js-modal-close');
   
   
   modalButtons.forEach(function(item){
      
      item.addEventListener('click', function(e) {
         
         e.preventDefault();

         var modalId = this.getAttribute('data-modal'),
             modalElem = document.querySelector('.modal[data-modal="' + modalId + '"]');
         
         modalElem.classList.add('active');
         overlay.classList.add('active');

      }); // end click
   }); // end foreach
}); // end ready

Кнопки должны открывать то модальное окно, к которому привязаны. Проверяем:

Демо модального окна
Демо модального окна

Осталось написать закрытие окон по клику на крестик. С перебором элементов и созданием события вы уже знакомы.

closeButtons.forEach(function(item) {

   item.addEventListener('click', function(e) {

   });

}); // end foreach

При клике на крестик нам нужно у этого же элемента найти родителя или деда с классом .modal и удалить у него класс .active.

Готовой функции для перебора родителей элемента нет. Есть метод который позволяет получить родителя, но это ненадежно, так как разметка может поменяться и наш код сломается.

Для таких задач я не буду писать велосипед, а воспользуюсь готовой микро-библиотекой closest. Код библиотеки:

!function(e){"function"!=typeof e.matches&&(e.matches=e.msMatchesSelector||e.mozMatchesSelector||e.webkitMatchesSelector||function(e){for(var t=this,o=(t.document||t.ownerDocument).querySelectorAll(e),n=0;o[n]&&o[n]!==t;)++n;return Boolean(o[n])}),"function"!=typeof e.closest&&(e.closest=function(e){for(var t=this;t&&1===t.nodeType;){if(t.matches(e))return t;t=t.parentNode}return null})}(window.Element.prototype);

Его нужно подключить в отдельном файле или в этом же файле до нашего кода.

Библиотека создает функцию closest. Используя её мы можем искать элемент, который находится выше по дереву и класс которого совпадает с тем который мы ищем.

В нашем случае мы должны найти родителя с классом .modal и не важно является ли он прямым предком или между них есть еще какие-то элементы.

От нашего крестика мы идем вверх и проверяем родителя на класс .modal, не подошел — берем родителя его родителя и опять проверяем, так до самого конца, до window, пока не найдем интересующий нас элемент. В библиотеке jquery такая функция есть изначально, пишется она так же.

closeButtons.forEach(function(item){

   item.addEventListener('click', function(e) {
      console.log(this.closest('.modal'));
   });

}); // end foreach
Элемент в консоли

При клике на крестик, код выполнился и мы увидели в консоли родителя или деда нашего крестика, элемент с классом.modal. Теперь мы можем убрать у него активный класс, сразу же уберем его и у подложки.

closeButtons.forEach(function(item){

   item.addEventListener('click', function(e) {
      var parentModal = this.closest('.modal');

      parentModal.classList.remove('active');
      overlay.classList.remove('active');
   });

}); // end foreach

Демонстрация работы:

Демо открытия и закрытия окна

Теперь вы можете создавать много много модальных окон не изменяя свой js код.

Демо на codepen

Для многих операция jquery является громоздкой библиотекой. Вы можете использовать вместо этого микро-библиотеки. Сборник библиотек — http://microjs.com/.

Еще один полезный сайт который уже в названии говорит что jquery вам может быть и не нужен. http://youmightnotneedjquery.com/

Спасибо за прочтение статьи! Если материал был полезен для тебя — поставь лайк или напиши об этом в комментариях, это займет 1 минуту, это важно для меня. Спасибо!

Если у тебя есть замечания, пожелания, предложения — можешь так же написать их в комментариях. Я рад новым мнениям и критике.

Дополнение

По просьбе читателя добавляю код для закрытия модального окна по кнопке ESC и при клике на темный фон.

Закрытие окна при клике на ESC:

document.body.addEventListener('keyup', function (e) {
    var key = e.keyCode;

    if (key == 27) {
        document.querySelector('.modal.active').classList.remove('active');
        document.querySelector('.overlay.active').classList.remove('active');
    };
}, false);

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

У событий связанных с клавишами, есть свойство keycode, которое хранит код нажатой клавиши. У ESC этот код — 27.

Нам остается проверять при каждом нажатии код клавиши с кодом ESC и если есть совпадение — удаляем класс у активного окна и фона.

Скрытие при нажатии

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

Не подглядывать

Не подглядывать

Не подглядывать

Ладно, подглядывай уже 🙂

overlay.addEventListener('click', function() {
    document.querySelector('.modal.active').classList.remove('active');
    this.classList.remove('active');
});

Создаем событие клика на overlay, эта переменная создана в самом начале. Потом в функции удаляем у активного модального окна активный класс и делаем тоже самое у фона, на который кликнули, за это отвечает переменная this, если переводить — этот.

То есть при клике на фон, мы говорим: «Удали активный класс у окна и у фона, на который кликнули»

Плагин для копирования -clipboard.js

Привет, в этой статье разберем плагин clipboard. js и варианты его применения.

Что будем делать. Я покажу как мы можем копировать текст, как сделать это на множестве кнопок, как сделать это если кнопки создавать не хочется.

Для начала работы нам нужно подключить плагин.
Страница плагина — https://clipboardjs.com/.
cdnjs — https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/2.0.4/clipboard.min.js

Подключаем плагин в начале, после него будем писать наш код. К счастью кода совсем мало — 1 строка:

var clipboard = new ClipboardJS('.js-btn-copy');

В параметре функции нужно указать класс, который будет кнопкой для копирования текст.
Мы поставим — .js-copy-btn.

Вот все, всем спасибо, расходимся … да шучу :-), еще немножко кода будет

Просто скопируем текст

Для копирования текста, нам нужно показать кнопке, откуда его нужно копировать. Для этого мы укажем в атрибуте класс или id того элемента, который нужно копировать. Сделаем это пошагово:

Создадим тег с текстом и кнопку:

<p>текст 3</p>
<button>Копировать</button>   

Кнопке, тексту укажем классы и добавим кнопке атрибут для копирования:

<p class="cb-target-1">текст 3</p>
<button class="js-btn-copy" data-clipboard-target=".cb-target-1">Копировать</button>  

Вот что получилось в итоге, в инпуте можете вставить скопированный текст:

See the Pen clipboard-1 article by Postnov Daniil (@dan_postnov) on CodePen.

Не очень интересно, не правда ли?

Сделаем несколько кнопок

Менять цифры у классов, в дата атрибуте … долго и нудно, программисты мы или кто!

Что сделаем. С помощью чистого Js или Jquery циклом переберем кнопки и теги с текстом и установим необходимые классы и дата атрибуты. Создадим чистую от clipboard.js разметку, добавив только классы для js:

<p class="js-copy-text">текст 1</p>
<button class="js-copy-btn">Копировать</button>

<p class="js-copy-text">текст 2</p>
<button class="js-copy-btn">Копировать</button>

<p class="js-copy-text">текст 3</p>
<button class="js-copy-btn">Копировать</button>

Теперь мы должны запустить 2 цикла, в которых добавим тексту класс, кнопке добавим дата атрибут с классом текстового тега:

$('.js-copy-text').each(function(i, item) {
    $(item).addClass('cb-target-' + i);
});

$('.js-copy-btn').each(function(i, item) {
    $(item).attr('data-clipboard-target','.cb-target-' + i);
});

Что получилось с разметкой в итоге: 

Плагин для копирования -clipboard.js 1

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

Вот что получилось:

See the Pen clipboard.js 2 by Postnov Daniil (@dan_postnov) on CodePen.

Добавляем кнопки после текста.

Совсем для ленивых программистов 🙂

Вернемся в исходную. Ставим разметку с базовыми классами и уберем кнопки:

<p class="js-copy-text">текст 1</p> 

<p class="js-copy-text">текст 2</p>  

<p class="js-copy-text">текст 3</p>

Что будем делать. Циклом перебираем текст, назначаем классы, каждую итерацию создаем кнопку, добавляем ей классы и атрибут, добавляем после кнопки. Поехали!:

$('.js-copy-text').each(function(i, item) {
   var btn = $('<button>Копировать</button>');
   btn.addClass('js-copy-btn');
   btn.attr('data-clipboard-target','.cb-target-' + i);

   $(item).addClass('cb-target-' + i);
   $(item).after(btn);
});

Создали кнопку и поместили ее в переменную btn. Этой кнопке добавили класс и атрибут data-clipboard с классом нашего текста. Добавили класс нашему тексту и вставили эту кнопку сразу за нашим текстом.

Демо:

See the Pen Overview clipboard. js by Postnov Daniil (@dan_postnov) on CodePen.

Немного о событиях

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

В clipboard есть 2 события: success и error

В событиях мы имеем 3 свойства:

  • e.action — показывает событие, которое произошло
  • e.text — скопированный текст
  • e.trigger — элемент, который отвечает за копирование текста

 Как это использовать?

Самое примитивное — показывать пользователю скопирован текст или нет. Сделать смену текста в кнопке и фоновый цвет кнопки.

Мы конечно можем сделать смену с помощью метода css в jquery, я так делать не советую, проще сделать добавление и удаление класса.

Создадим класс .is-copy для успешного копирования, покрасим кнопку в зеленый и сделаем белый цвет текста:

.is-copy {
   background-color: green;
   color: #fff;
}

И при успешном копировании будем добавлять класс к кнопке и менять ее текст:

clipboard.on('success', function(e) {
   $(e.trigger).text('Скопировано');
   $(e.trigger).addClass('is-copy');
});

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

clipboard.on('success', function(e) {
   $('.js-copy-btn.is-copy').removeClass('is-copy').text('Копировать');
   
   $(e.trigger).text('Скопировано');
   $(e.trigger).addClass('is-copy');
});

Вот что получилось в итоге:

See the Pen Clipboard 4 article by Postnov Daniil (@dan_postnov) on CodePen.

Для события error вы сможете сами сделать обработку.

Доступ на сайт по паролю. 5 строк кода

В этот статье поговорим об том, как сделать доступ к сайту или отдельной директории по паролю.

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

.htaccess — файл дополнительной конфигурации веб-сервера Apache.
.htpasswd —
файл для указания логинов и паролей для доступа.

Расположение этих файлов будет зависеть от того, к какой директории вы хотите закрыть доступ.

Закрыть доступ ко всему сайту

Файл .htaccess должен находится в корневой папке сайта, .htpasswd лучше положить в какую-то из директорий, чтобы до него нельзя было так просто добраться — там будут лежать логины и пароли для доступа. Предлагаю положить его в директорию /folder.

Получается такая структура:

Доступ на сайт по паролю. 5 строк кода 2

В файл. htaccess вставляем код:

AuthType Basic
AuthName "Protected Area"
AuthUserFile /.htpasswd
Require valid-user

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

AuthType — типы аутентификации: Basic или Digest. Рекомендуется использовать первый, т.к. второй поддерживается не всеми браузерами.

AuthUserFile — полный путь к файлу с логинами и паролями, для аутентификации пользователей. Пароли содержаться в шифрованном виде. Рекомендуется хранить данный файл в папке, к которой нет доступа для пользователей, это необходимо, чтобы предотвратить кражу паролей.

require valid-user — директива предписывает, что к URL получают доступ только, пользователи, успешно прошедшие аутентификацию.

В строке AuthUserFile вставляете путь, который получился у вас в файле test. php

Для того чтобы узнать абсолютный путь к файлу, создаем в директории с файлом. htpasswd файл test. php, в нем экранируем переменную:

<?php echo dirname($_SERVER['SCRIPT_FILENAME']);

Теперь нам нужно открыть этот файл в браузере.
ваш-сайт.ru/folder/test.php, подставите адрес своего сайта.


У меня на выходе получилась такая строка
/home/***/works/sneakers-html/folder

Вставляем ее в код и получаем:

AuthType Basic
AuthName "Protected Area"
AuthUserFile /home/***/works/sneakers-html/folder/.htpasswd
Require valid-user

После того как вы узнали абсолютный путь до директории, удалите этот файл, он больше не нужен.

Теперь нам нужно создать зашифрованный пароль. Для этого перейдем по ссылке на генератор паролей.

Заполняем необходимые данные в форме и нажимаем кнопку «сгенерировать»:

Доступ на сайт по паролю. 5 строк кода 3

Скопируем получившуюся строку и вставим в файл. htpasswd:

Доступ на сайт по паролю. 5 строк кода 4
содержимое файла. htpasswd

Строка означает — логин: пароль в зашифрованном виде. Для доступа на сайт нам нужно вводить тот логин и пароль, который мы указывали в генераторе, то есть admin и password.

Что мы имеем в итоге:

  • файл .htaccess лежащий в корне сайта с 4 строками кода и путем к файлу с логинами и паролями
  • файл .htpasswd с логином и паролем

Теперь мы можем заходить на сайт и должны увидеть такое окно:

Доступ на сайт по паролю. 5 строк кода 5
Форма для ввода логина и пароля

После ввода данных на откроется главная страница сайта. Если же будет ошибка в вводимых данных, либо мы получим 404 ошибку, либо страница перезагрузится и снова появится форма ввода данных.

Закрыть доступ к директории

Разница между этими способами в том, что файл. htaccess нужно поместить в ту директорию, которую вам необходимо закрыть:

Доступ на сайт по паролю. 5 строк кода 6

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

Будьте внимательны

Любое несоответствие кода в .htaccess или неправильный путь до файла приведут к ошибке «500 Internal Server Error». Если это произошло — сверяйте код в файле .htaccess

У меня выкидывало ошибку из-за отсутствия «/» в начале пути.


Спасибо за прочтение статьи! Если у вас возникнут какие-то вопросы, трудности, пишите мне на почту, в личку в вк, в комментариях, по возможности помогу!