基於原生JS封裝的Modal對話框插件

基於原生JS封裝Modal對話框插件

原生JS封裝Modal對話框插件,我的用來學習原理與思想,只有簡單的基本框架的實現,可在此基礎上添加更多配置項javascript

API配置

//基本語法
    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(...)

Modal插件核心功能的開發

導出

(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)

    }

命令模式init()執行邏輯

file

建立DOM

//建立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
        }

對參數進行處理

file

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'
        }

基於事件委託處理點擊事件

file

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
                }

            })
        },

基於發佈訂閱實現回調函數的監聽(生命週期)

file

file
file

//使用:
file
filevue

完整代碼

//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.jsmodalpugin.cssjava

使用示例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:
filereact

github

完整代碼githubwebpack

相關文章
相關標籤/搜索