基于原生JS封装的Modal对话框插件的示例代码

来自:互联网
时间:2020-10-14
阅读:

基于原生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()执行逻辑

基于原生JS封装的Modal对话框插件的示例代码

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

对参数进行处理

基于原生JS封装的Modal对话框插件的示例代码

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

基于事件委托处理点击事件

基于原生JS封装的Modal对话框插件的示例代码

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
        }

      })
    },

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

基于原生JS封装的Modal对话框插件的示例代码

基于原生JS封装的Modal对话框插件的示例代码
基于原生JS封装的Modal对话框插件的示例代码

//使用:
基于原生JS封装的Modal对话框插件的示例代码
基于原生JS封装的Modal对话框插件的示例代码

完整代码

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

基于原生JS封装的Modal对话框插件的示例代码

返回顶部
顶部