寫一個單例構造的對話框

前言

項目中,當須要用戶處理事務,又不但願跳轉頁面以至打斷工做流程時,咱們會常用到對話框去承載相應的操做。雖然用得多,可是不少人其實並不知道怎麼去寫。饒有興趣,本身嘗試寫了一個。css

API

參數 說明 類型 默認值
afterClose Modal 徹底關閉後的回調 function
cancelText 取消按鈕文字 string 取消
closable 是否顯示右上角的關閉按鈕 boolean true
destroyOnClose 關閉時銷燬 Modal boolean false
footer 底部內容,當不須要默認底部按鈕時,能夠設爲 false boolean true
maskClosable 點擊蒙層是否容許關閉 boolean true
okText 確認按鈕文字 string 肯定
title 標題 string|html節點 標題
width 寬度 number 500
zIndex 設置 Modal 的 z-index number 1000
keyboard 是否支持鍵盤esc關閉 boolean true
onCancel 點擊遮罩層或右上角叉或取消按鈕的回調 function(e)
onOk 點擊肯定回調 function(e)

methods

參數 說明 使用形式
set 已覆蓋默認的形式,設置新的屬性,內部 Object.assign(this.options, newOptions) model.set(newOptions)
open 打開對話框 model.open()
close 關閉對話框 model.close()
destroy 與close的區別在於,會從節點上刪除 model.destroy()

代碼

對話框全局中使用一個就夠了,爲了防止生成多個對話框,咱們使用單例構造html

var Dialog = (function () {
    var instance;
    return function (options) {
        if (!instance) {
            instance = new Modal(options);
        }
        return instance;
    }
})()

這裏主要運用到了閉包中的特性閉包

modal.js代碼app

var Dialog = (function () {
    var instance;
    return function (options) {
        if (!instance) {
            instance = new Modal(options);
        }
        return instance;
    }
})()

class Modal {
    constructor(options) {
        //默認屬性
        this.initOptions = {
            title: '標題',
            maskClosable: true,
            header: true,
            footer: true,
            closable: true,
            okText: '確 定',
            cancelText: '取 消',
            destroyOnClose: false,
            keyboard: true,
            zIndex: 1000,
            width: 500,
            afterClose: null
        }
        this.options = Object.assign({}, this.initOptions, options);

        this.instance = document.createElement('div');//節點實例,由於只須要一個模態框,這裏設置節點就能夠了
        this.mounted = false; //是否掛載在節點上
    }

    //處理模態框的點擊事件
    _modalClick(e) {
        var className = e.target.className;
        //匹配類名,例如 帶有 class='modal-close' 的元素點擊能夠關閉模態框
        if (new RegExp("(\\s|^)modal-close(\\s|$)").test(className)) {
            this.cancel();   //關閉模態框
        } else if (new RegExp("(\\s|^)modal-onOk(\\s|$)").test(className)) {
            this.onOk();     //執行肯定按鈕的回調
        } else if (new RegExp("(\\s|^)modal-container(\\s|$)").test(className)) {
            this.outSideClick(); //模態框外的點擊
        }
    }

    //處理鍵盤ESC關閉
    _escClose(e) {
        var code = e.keyCode;
        if (code === 27) {
            this.cancel();
        }
    }

    //渲染模態框節點
    render() {
        var modal = this.instance;
        modal.style.zIndex = this.options.zIndex;
        modal.className = 'modal-container';
        var closeIcon = this.options.closable ? `<span class="modal-close">X</span>` : '';
        var header = this.options.header ?
            (this.options.header === true ?
                `<div class='modal-header'>${this.options.title}
                                    ${closeIcon}
                                </div>` :
                this.options.header) :
            '';
        var footer = this.options.footer ?
            (this.options.footer === true ?
                `<div class='modal-footer'>
                                <span class="modal-btn modal-close">${this.options.cancelText}</span>
                                <span class="modal-btn  modal-onOk modal-btn-primary">${this.options.okText}</span>
                            </div>` :
                this.options.footer) :
            '';
        modal.innerHTML = `<div class="modal" style="width: ${this.options.width}px">
                                ${header}
                                <div class='modal-content'>${this.options.content}</div>
                                ${footer}
                            </div>`;
    }


    //蒙層點擊關閉
    outSideClick() {
        if (this.options.maskClosable) {
            this.close();
        }
    }

    //處理監聽
    listen() {
        this._modalClick = this._modalClick.bind(this);
        this.instance.addEventListener('click', this._modalClick);
        
        if(this.options.keyboard){
            this._escClose = this._escClose.bind(this);
            window.addEventListener('keyup', this._escClose);
        }
    }

    cancel(){
        if(typeof this.options.onCancel === "function"){
            this.options.onCancel();
        }
        this.close();
    }

    //點擊肯定回調
    onOk() {
        this
            .options
            .onOkFn();
        this.close();
    }


    /****************** 提供的方法  ********************* */
    //設置屬性
    set(options) {
        Object.assign(this.options, options)
        this.render()
    }

    //打開模態框
    open() {
        var modal = this.instance;
        //實例若是沒掛載
        if (!this.mounted) {
            this.mounted = true;
            this.render();
            document.body.appendChild(modal);
            this.listen()
        }
        removeClass(modal, 'close');
        addClass(modal, 'open');
    }

    //關閉模態框
    close() {
        var modal = this.instance
        removeClass(modal, 'open');
        addClass(modal, 'close');
        if (this.options.destroyOnClose === true) {
            this.destroy();
        }
        if (typeof this.options.afterClose === "function") {
            var afterClose = this.options.afterClose.bind(this);
            setTimeout(afterClose, 0);
        }
    }
    //從節點上移除模態框
    destroy() {
        this.instance.removeEventListener('click', this._modalClick);//移除click監聽
        window.removeEventListener('keyup', this._escClose);//移除keyup監聽
        document.body.removeChild(this.instance);//移除模態框節點
        this.mounted = false;
    }

}

function hasClass(elements, cName) {
    return !!elements
        .className
        .match(new RegExp("(\\s|^)" + cName + "(\\s|$)"));
};
function addClass(elements, cName) {
    if (!hasClass(elements, cName)) {
        elements.className += " " + cName;
    };
};
function removeClass(elements, cName) {
    if (hasClass(elements, cName)) {
        elements.className = elements
            .className
            .replace(new RegExp("(\\s|^)" + cName + "(\\s|$)"), "");
    };
};

modal.csside

@keyframes openAnimate {
    0% {
        opacity: 0;
    }
    100%{
        opacity: 1;
    }
}

.open{
    display: block !important;
    animation: openAnimate .8s;
}
.close{
    display: none;
}
.modal-container {
    position: fixed;
    top: 0;
    right: 0;
    left: 0;
    bottom: 0;
    background-color: #373737;
    background-color: rgba(0,0,0,.65);
    margin: auto;
    filter: alpha(opacity=50);
    text-align: center;
    font-size: 0;
    white-space: nowrap;
    overflow: auto;
    display: none;
}

.modal-container:after {
    content: '';
    display: inline-block;
    height: 100%;
    vertical-align: middle;
}

.modal {
    display: inline-block;
    vertical-align: middle;
    text-align: left;
    font-size: 14px;
    background-color: #fff;
    border: 0;
    border-radius: 4px;
    background-clip: padding-box;
    box-shadow: 0 4px 12px rgba(0,0,0,.15);
}

.modal-header{
    padding: 16px 24px;
    border-radius: 4px 4px 0 0;
    background: #fff;
    color: rgba(0,0,0,.65);
    border-bottom: 1px solid #e8e8e8;
}
.modal-close{
    float: right;
    cursor: pointer;
}
.modal-content{
    padding: 24px;
    font-size: 14px;
    line-height: 1.5;
    word-wrap: break-word;
    min-height: 200px;
}

.modal-footer {
    border-top: 1px solid #e8e8e8;
    padding: 10px 16px;
    text-align: right;
    border-radius: 0 0 4px 4px;
}

.modal-btn{
    line-height: 32px;
    display: inline-block;
    font-weight: 400;
    text-align: center;
    touch-action: manipulation;
    cursor: pointer;
    background-image: none;
    border: 1px solid transparent;
    white-space: nowrap;
    padding: 0 15px;
    font-size: 14px;
    border-radius: 4px;
    height: 32px;
    user-select: none;
    transition: all .3s cubic-bezier(.645,.045,.355,1);
    position: relative;
    color: rgba(0,0,0,.65);
    background-color: #fff;
    border-color: #d9d9d9;
    cursor: pointer;
}

.modal-btn-primary{
    color: #fff;
    background-color: #1890ff;
    border-color: #1890ff;
}

如何使用

<!DOCTYPE html>
<html lang="en">

    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>Document</title>
        <link rel="stylesheet" href="./modal.css">
    </head>

    <body>
        <button class="open">1</button>
        <button class="open">2</button>
        <button class="open">3</button>
        <script src="./modal.js"></script>
        <script src="./index.js"></script>
    </body>

</html>

index.jsui

var config = {
    title: '模態框標題',
    content: '',
    header: true,
    footer: true,
    destroyOnClose: true,
    onOkFn: function () {
        alert('提交成功')
    },
    afterClose: function(){
        alert('已經關閉')
    }
}
var modal = new Dialog(config);
var openBtns = document.querySelectorAll('.open');
openBtns.forEach(function (item, index) {
    item.onclick = function () {
        var option = {
            content: '這是第' + (index + 1) + '個'
        }
        modal.set(option)
        modal.open();
    }

})

圖片描述

相關文章
相關標籤/搜索