JSFiddle - React, Tailwind, and code Playground

by annndrewww

HTML

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css">
    <link rel="stylesheet" href="style.css">
    <title>Modal Window</title>
</head>
<body>
    <div class="wrap">
        <h1 class="title">JavaScript Modal Window</h1>
        <a href="#" data-hystmodal="#myModal">Open window</a>

        <!-- Modal window -->
        <div class="hystmodal" id="myModal" aria-hidden="true" >
            <div class="hystmodal__wrap">
                <div class="hystmodal__window" role="dialog" aria-modal="true" >
                    <button data-hystclose class="hystmodal__close">Close</button>  
                    <h1>Заголовок модального окна</h1>
                    <p>Текст модального окна ...</p>
                    <p>Ещё текст модального окна ...</p>
                </div>
            </div>
        </div>

        <!-- Modal Window end -->
    </div>

    <div class="modal-shadow"></div>

    <script src="app.js"></script>
</body>
</html>

CSS

/* settings */
ul {
    list-style: none;
}

/* Modal window */
.wrap {
    max-width: 1110px;
    margin: auto;
    padding: 30px 0;
}

.title {
    text-align: center;
    font-size: 60px;
}

.hystmodal {
    position: fixed;
    top: 0;
    bottom: 0;
    right: 0;
    left: 0;
    overflow: hidden;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    display: flex;
    flex-flow: column nowrap;
    justify-content: center; /* см. ниже */
    align-items: center;
    z-index: 99;
    visibility: hidden;
    /* Чтобы окно не прилипало к границе
    браузера установим отступы */
    padding:30px 0;
    background-color: rgba(0, 0, 0, .5);
}

.hystmodal__window {
    border-radius: 10px;
    padding: 20px;
    margin: 50px 0;
    flex-shrink: 0;
    flex-grow: 0;
    background: #fff;
    width: 600px;
    max-width: 100%;
    overflow: visible;
    transition: transform 0.2s ease 0s, opacity 0.2s ease 0s;
    transform: scale(0.9);
    opacity: 0;
}

.hysmodal__wrap {
    flex-shrink: 0;
    flex-grow: 0;
    width: 100%;
    min-height: 100%;
    margin: auto;
    display: flex;
    flex-flow: column nowrap;
    align-items: center;
    justify-content: center;
}

.btns {
    display: flex;
    justify-content: flex-end;
}

.hystmodal__close {
    font-size: 20px;
    border: none;
    padding: 9px 22px;
    border-radius: 15px;
    transition: .1s linear;
}

.hystmodal__close:hover {
    background-color: rgba(0, 0, 0, 0.1);
}

/* Modal active */
.hystmodal--active{
    visibility: visible;
}

.hystmodal--active .hystmodal__window {
    transform: scale(1);
    opacity: 1;
}

.hystmodal__shadow {
    position: fixed;
    border:none;
    display: block;
    width: 100%;
    top: 0;
    bottom: 0;
    right: 0;
    left: 0;
    overflow: hidden;
    pointer-events: none;
    z-index: 98;
    opacity: 0;
    transition: opacity 0.15s ease;
    background-color: black;
}

.hystmodal__shadow--show{
    pointer-events: auto;
    opacity:...

JavaScript

// Находим тег html и сохраняем его
let html = document.documentElement;
//сохраним текущую прокрутку:
let scrollPosition = window.pageYOffset;
//установим свойство top у html равное прокрутке
html.style.top = -scrollPosition + "px";
html.classList.add("hystmodal__opened");

html.classList.remove("hystmodal__opened");
//прокручиваем окно туда где оно было
window.scrollTo(0, scrollPosition);
html.style.top = "";

class HystModal{
    /**
     * При создании экземпляра класса, мы передаём в него
     * js-объект с настройками. Он становится доступен
     * в конструкторе класса в виде переменной props
     */
    constructor(props){
        /**
         * Для удобства некоторые свойства можно не передавать
         * Мы должны заполнить их начальными значениями
         * Это можно сделать применив метод Object.assign
         */
        let defaultConfig = {
            linkAttributeName: 'data-hystmodal',
            // ... здесь остальные свойства
        }
        this.config = Object.assign(defaultConfig, props);

        // сразу вызываем метод инициализации
        this.init();
    }

    /** 
     * В свойство _shadow будет заложен div с визуальной
     * подложкой. Оно сделано статическим, т.к. при создании
     * нескольких экземпляров класса, эта подложка нужна только
     * одна
     */
    static _shadow = false;

    init(){
        /**
         * Создаём триггеры состояния, полезные переменные и.т.д.
         */
        this.isOpened = false; // открыто ли окно
        this.openedWindow = false; //ссылка на открытый .hystmodal
        this._modalBlock = false; //ссылка на открытый .hystmodal__window
        this.starter = false, //ссылка на элемент "открыватель" текущего окна
        // (он нужен для возвращения фокуса на него)
        this._nextWindows = false; //ссылка на .hystmodal который нужно открыть
        this._scrollPosition = 0; //текущая прокрутка (см. выше)

        /**
         * ... остальное
         */

        // Создаём только одну...