原生JS封裝Modal對話框插件,我的用來學習原理與思想,只有簡單的基本框架的實現,可在此基礎上添加更多配置項javascript
//基本語法 let modal = ModalPlugin({ //提示的標題信息 title:'系統提示', //內容模板 字符串 /模板字符串/DOM元素對象 template:null, //自定義按鈕信息 buttons:[{ //按鈕文字 text:'肯定', click(){ //this:當前實例 } }] }) modal.open()//=>打開 modal.close()//=>關閉 //基於發佈訂閱,實現回調函數的監聽 modal.on('input/open/close/dragstart/dragmove/dragend',[func]) modal.fire(...) modal.off(...)
(function () { function ModalPlugin() { return } // 瀏覽器直接導入,這樣的方法是暴露到全局的 window.ModalPlugin = ModalPlugin; //若是還須要支持ES6Module/CommonJS模塊導入規範,在react項目當中,vue項目當中也想用 if (typeof module !== 'undefined' && module.exports !== 'undefined') {//若是module不存在,typeof不會出錯,會返回undefined module.exports = ModalPlugin;//CommonJS規範,只有在webpack環境下才支持 } })()
想使用建立對象的方式new ModalPlugin()
建立實例或當作普通函數執行ModalPlugin()
,建立實例,須要這樣作css
(function () { function ModalPlugin() { return new init() } //想使用建立對象的方式`new ModalPlugin()`建立實例或當作普通函數執行`ModalPlugin()`,建立實例,須要這樣作 //類的原型: 公共的屬性方法 ModalPlugin.prototype = { constructor: ModalPlugin } function init() {} init.prototype = ModalPlugin.prototype; // 瀏覽器直接導入,這樣的方法是暴露到全局的 window.ModalPlugin = ModalPlugin; //若是還須要支持ES6Module/CommonJS模塊導入規範,在react項目當中,vue項目當中也想用 if (typeof module !== 'undefined' && module.exports !== 'undefined') {//若是module不存在,typeof不會出錯,會返回undefined module.exports = ModalPlugin;//CommonJS規範,只有在webpack環境下才支持 } })()
//封裝插件的時候,須要支持不少配置項,有的配置項不傳遞有默認值,此時咱們千萬不要一個個定義形參,用對象的方式傳形參,好處是能夠不傳,並且能夠不用考慮順序 function ModalPlugin(options) { return new init(options) } //想使用建立對象的方式建立實例new ModalPlugin()或當作普通函數執行也能建立實例ModalPlugin(),須要這樣作 ModalPlugin.prototype = { constructor: ModalPlugin } function init(options) { //接下來將全部的操做所有寫在init裏面 //參數初始化:傳遞進來的配置項替換默認的配置項 options = Object.assign({ title:'系統提示', template:null, frag:true, buttons:[{ text:'肯定', click(){ } }] },options) }
//建立DOM結構 creatDom(){ //若是用creatElement插入DOM,每一次動態插入,都會致使DOM的迴流,很是消耗性能,因此最外面使用createElement建立,內部使用字符串的方式拼寫進去,建立好了以後放到最外層的容器當中,只引發一次迴流 let frag = document.createDocumentFragment() let dpnDialog = document.createElement('div') dpnDialog.className = 'dpn-dialog' dpnDialog.innerHTML = ` <div class="dpn-title"> 系統舒適提示 <i class="dpn-close"></i> </div> <div class="dpn-content"> </div> <div class="dpn-handle"> <button>肯定</button> <button>取消</button> </div>` frag.appendChild(dpnDialog) let dpnModel = document.createElement('div') dpnModel.className = 'dpn-model' frag.appendChild(dpnModel) document.body.appendChild(frag)//使用frag只須要往頁面中插入一次,減小回流次數 frag = null this.dpnDialog = dpnDialog//掛載到實例上,便於其餘方法的控制隱藏,而且是私有的實例, this.dpnModel = dpnModel }
creatDom() { let {title, template, buttons} = this.options //若是用creatElement插入DOM,每一次動態插入,都會致使DOM的迴流,很是消耗性能,因此最外面使用createElement建立,內部使用字符串的方式拼寫進去,建立好了以後放到最外層的容器當中,只引發一次迴流 let frag = document.createDocumentFragment() let dpnDialog = document.createElement('div') dpnDialog.className = 'dpn-dialog' dpnDialog.innerHTML = ` <div class="dpn-title"> ${title} <i class="dpn-close">X</i> </div> <div class="dpn-content"> ${template && typeof template === 'object' && template.nodeType === 1 ? template.outerHTML : template} </div> ${buttons.length > 0 ? `<div class="dpn-handle"> ${buttons.map((item, index) => { return `<button index="${index}">${item.text}</button>` }).join('')} </div>` : '' } ` frag.appendChild(dpnDialog) let dpnModel = document.createElement('div') dpnModel.className = 'dpn-model' frag.appendChild(dpnModel) document.body.appendChild(frag)//使用frag只須要往頁面中插入一次,減小回流次數 frag = null this.dpnDialog = dpnDialog//掛載到實例上,便於其餘方法的控制隱藏,而且是私有的實例, this.dpnModel = dpnModel },
//控制他顯示 open() { this.dpnDialog.style.display = 'block' this.dpnModel.style.display = 'block' }, //控制隱藏 close() { this.dpnDialog.style.display = 'none' this.dpnModel.style.display = 'none' }
init() { this.creatDom() //基於事件委託,實現點擊事件的處理 this.dpnDialog.addEventListener('click', (ev)=>{ let target = ev.target, {tagName,className}= target console.log([target]) //點擊的關閉按鈕 if(tagName==='I'&&className.includes('dpn-close')){ this.close() return } //點擊的是底部按鈕 if(tagName==='BUTTON' && target.parentNode.className.includes('dpn-handle')){ let index = target.getAttribute('index') //讓傳過來的函數執行,而且函數中的this還必須是當前實例 let func = this.options.buttons[index]['click'] if(typeof func==='function'){ func.call(this) } return } }) },
//使用:
vue
//modalplugin.js (function () { //封裝插件的時候,須要支持不少配置項,有的配置項不傳遞有默認值,此時咱們千萬不要一個個定義形參,用對象的方式傳形參,好處是能夠不穿,並且能夠不用考慮順序 function ModalPlugin(options) { return new init(options) } //想使用建立對象的方式建立實例new ModalPlugin()或當作普通函數執行也能建立實例ModalPlugin(),須要這樣作 ModalPlugin.prototype = { constructor: ModalPlugin, //至關於大腦,能夠控制先幹什麼在幹什麼(命令模式) init() { //建立DOM結構 this.creatDom() //基於事件委託,實現點擊事件的處理 this.dpnDialog.addEventListener('click', (ev) => { let target = ev.target, {tagName, className} = target //點擊的關閉按鈕 if (tagName === 'I' && className.includes('dpn-close')) { this.close() return } //點擊的是底部按鈕 if (tagName === 'BUTTON' && target.parentNode.className.includes('dpn-handle')) { let index = target.getAttribute('index') //讓傳過來的函數執行,而且函數中的this還必須是當前實例 let func = this.options.buttons[index]['click'] if (typeof func === 'function') { func.call(this) } return } }) this.fire('init')//通知init方法執行成功 }, //建立DOM結構 creatDom() { let {title, template, buttons} = this.options //若是用creatElement插入DOM,每一次動態插入,都會致使DOM的迴流,很是消耗性能,因此最外面使用createElement建立,內部使用字符串的方式拼寫進去,建立好了以後放到最外層的容器當中,只引發一次迴流 let frag = document.createDocumentFragment() let dpnDialog = document.createElement('div') dpnDialog.className = 'dpn-dialog' dpnDialog.innerHTML = ` <div class="dpn-title"> ${title} <i class="dpn-close">X</i> </div> <div class="dpn-content"> ${template && typeof template === 'object' && template.nodeType === 1 ? template.outerHTML : template} </div> ${buttons.length > 0 ? `<div class="dpn-handle"> ${buttons.map((item, index) => { return `<button index="${index}">${item.text}</button>` }).join('')} </div>` : '' } ` frag.appendChild(dpnDialog) let dpnModel = document.createElement('div') dpnModel.className = 'dpn-model' frag.appendChild(dpnModel) document.body.appendChild(frag)//使用frag只須要往頁面中插入一次,減小回流次數 frag = null this.dpnDialog = dpnDialog//掛載到實例上,便於其餘方法的控制隱藏,而且是私有的實例, this.dpnModel = dpnModel }, //控制他顯示 open() { this.dpnDialog.style.display = 'block' this.dpnModel.style.display = 'block' this.fire('open')//通知open方法執行成功 }, //控制隱藏 close() { this.dpnDialog.style.display = 'none' this.dpnModel.style.display = 'none' this.fire('close')//通知close方法執行成功 }, //on向事件池中訂閱方法 on(type, func) { let arr = this.pond[type] if(arr.includes(func)) return arr.push(func) }, //通知事件池中的方法執行 fire(type) { let arr = this.pond[type] arr.forEach(item => { if(typeof item ==='function'){ item.call(this) } }) } } function init(options) { //接下來將全部的操做所有寫在init裏面 //參數初始化:傳遞進來的配置項替換默認的配置項 options = Object.assign({ title: '系統提示', template: null, frag: true, buttons: [{}] }, options) //把信息掛載到實例上: 在原型的各個方法中,只要this是實例,均可以調用到這些信息 this.options = options; this.pond = { init: [], close: [], open: [] } this.init() } init.prototype = ModalPlugin.prototype; // 瀏覽器直接導入,這樣的方法是暴露到全局的 window.ModalPlugin = ModalPlugin; //若是還須要支持ES6Module/CommonJS模塊導入規範,在react項目當中,vue項目當中也想用 if (typeof module !== 'undefined' && module.exports !== 'undefined') {//若是module不存在,typeof不會出錯,會返回undefined module.exports = ModalPlugin;//CommonJS規範,只有在webpack環境下才支持 } })()
使用時須要引入modalpugin.js
和modalpugin.css
java
使用示例1:node
//使用: const modal1 = ModalPlugin({ //提示的標題信息 title: '系統提示', //內容模板 字符串 /模板字符串/DOM元素對象 template: null, //自定義按鈕信息 buttons: [{ //按鈕文字 text: '肯定', click() { //this:當前實例 this.close() } }, { //按鈕文字 text: '取消', click() { //this:當前實例 this.close() }, }] }) modal1.on('open',()=>{ console.log('我被打開了1') }) modal1.on('open',()=>{ console.log('我被打開了2') }) modal1.on('close',()=>{ console.log('我被關閉了') }) modal1.open()
使用示例2:
react
完整代碼githubwebpack