乐趣区

关于javascript:基于原生JS封装的Modal对话框插件

基于原生 JS 封装 Modal 对话框插件

原生 JS 封装 Modal 对话框插件,集体用来学习原理与思维,只有简略的根本框架的实现,可在此基础上增加更多配置项

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(), 创立实例, 须要这样做

(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()执行逻辑

创立 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
        }

对参数进行解决

      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
                }

            })
        },

基于公布订阅实现回调函数的监听(生命周期)


// 应用:

残缺代码

//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.css

应用示例 1:

// 应用:
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:

github

残缺代码 github

退出移动版