項目中,當須要用戶處理事務,又不但願跳轉頁面以至打斷工做流程時,咱們會常用到對話框去承載相應的操做。雖然用得多,可是不少人其實並不知道怎麼去寫。饒有興趣,本身嘗試寫了一個。css
參數 | 說明 | 類型 | 默認值 |
---|---|---|---|
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) | 無 |
參數 | 說明 | 使用形式 |
---|---|---|
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(); } })