js 写插件入门(弹出框)

阅读 (306)
原生JS写插件,本例重点在于对this作用域的理解

一千个人眼中有一千个哈姆雷特,同样写插件实现具体的功能也有很多种方法,以下代码只是个人成长中的随笔

直接看样例:https://returnc.com/demo/alert.html

;(function(undefined) {
      "use strict"
      var _global
      // 合并对象(深拷贝)
      function extend(defaults, n) {
        var n = n || {};
        for (var x in defaults) {
          // 对于使用时,没有设置的参数;用默认参数代替
          if (typeof n[x] === 'undefined') {
            n[x] = defaults[x];
          }
        }
        return n
      }

      /*
      * 监听函数
      * obj 监听对象
      * type 监听事件
      * handle 执行函数
      */
      function addEvent(obj,type,handle){
        try{
          obj.addEventListener(type,handle,false);
        }catch(e){
          try{
          obj.attachEvent('on'+type,handle);
          }
          catch(e){
          obj['on' + type]=handle;//早期浏览器
          }
        }
      }

      /* 插件函数
      * 参数1:弹框标题
      * 参数2:弹框内容
      * 参数3:自定义配置
      * 参数4:关闭前的回调函数
      */ 参数5:关闭后的回调函数
      function KAlert(title, content, opt, beforeClose, afterClose){
        // 未使用new关键词时
        if (!(this instanceof KAlert)){
          // 等于再new一个KAlert
          return new KAlert(title, content, opt, beforeClose, afterClose)
        }
        this._initial(title, content, opt, beforeClose, afterClose)
      }

      KAlert.prototype = {
        constructor: this,
        // 插件初始化
        _initial: function (title, content, opt, beforeClose, afterClose) {
          // 默认配置
          var defaults = {
            showCancelBtn: true, // 是否显示“取消按钮”,即左侧按钮
            showConfirmBtn: true, // 是否显示“确定按钮”,即右侧按钮
            cancelBtnText: '取消', // 左侧按钮文字
            confirmBtnText: '确定', // 右侧按钮文字
            cancelBtnClassName: 'kalert-cancel-btn', // 左侧按钮样式名
            confirmBtnClassName: 'kalert-confirm-btn', // 右侧按钮样式名
            cancelBtnColor: '#d90000', // 左侧按钮文字默认颜色
            confirmBtnColor: '#333333', // 右侧按钮文字默认颜色
            title: '', // 弹框标题
            content: '' // 弹框内容
          }

          // 没有设置的参数,使用默认值
          this.opt = extend(defaults, opt) // 得到的this.opt, 在方法中调用;
          this.opt.title = title || '提示' // 弹框标题
          this.opt.content = content || '' // 弹框内容
          this.beforeClose = beforeClose || function() {} // 设置用户回调函数:关闭前的回调事件
          this.afterClose = afterClose || function() {} // 设置用户回调函数:关闭后的回调事件
          // 弹框DOM元素创建
          this._renderDOM()

        },
        // 渲染弹窗 DOM 结构
        _renderDOM: function() {
          // 创建元素
          var alertMask = document.createElement("div")
          var alertWrapper = document.createElement("div")
          var alertBox = document.createElement("div")
          var title = document.createElement("h3")
          var content =  document.createElement("div")
          var footer =  document.createElement("div")
          var cancelBtn = document.createElement("button")
          var confirmBtn = document.createElement("button")

          // 样式名
          alertMask.className = 'kalert-mask'
          alertWrapper.className = 'kalert-wrapper'
          alertBox.className = 'kalert-box'
          title.className = 'kalert-title'
          content.className = 'kalert-content'
          footer.className = 'kalert-footer'
          cancelBtn.className = this.opt.cancelBtnClassName
          confirmBtn.className = this.opt.confirmBtnClassName

          // 设置自定义样式
          cancelBtn.setAttribute('style', 'color: ' + this.opt.cancelBtnColor);
          confirmBtn.setAttribute('style', 'color: ' + this.opt.confirmBtnColor);

          // 设置标题、内容、按钮
          title.innerHTML = this.opt.title
          content.innerHTML = this.opt.content
          cancelBtn.innerHTML = this.opt.cancelBtnText
          confirmBtn.innerHTML = this.opt.confirmBtnText

          // 填充元素
          if(this.opt.showCancelBtn) {
            footer.appendChild(cancelBtn)
          }
          if(this.opt.showConfirmBtn) {
            footer.appendChild(confirmBtn)
          }
          alertWrapper.appendChild(alertBox)
          alertMask.appendChild(alertWrapper)
          alertBox.appendChild(title)
          alertBox.appendChild(content)
          alertBox.appendChild(footer)

          document.body.appendChild(alertMask)

          // addEvent _onCancel 和 _onConfirm 方法内部的this指向了按钮对象,这里用bind重新指向到this,即 KAlert
          addEvent(cancelBtn, 'click', this._onCancel.bind(this))
          addEvent(confirmBtn, 'click', this._onConfirm.bind(this))

          // body上加个特有样式
          document.body.classList.add('kalert-show')
        },

        // 点击关闭按钮
        _onCancel: function () {
          // 如果换成箭头函数,下面用到的this则指向Kalert, 用function()则指向点击对象本身, 这里使用function提高兼容性,防止浏览器不支持ES6以上的语法
          // 但在此例中在绑定监听事件后,我又修改了this指向,使这里的this又指向了Kalert
          console.log(`您点击了${this.opt.cancelBtnText}按钮`)
          this._close()
        },
        // 点击确定按钮
        _onConfirm: function () {
          console.log(`您点击了${this.opt.confirmBtnText}按钮`)
          this._close()
        },
        // 关闭提示框
        _close: function() {
          this.beforeClose()
          // 移除body上的className
          document.body.classList.remove('kalert-show')
          // 销毁DOM,由于直接写了指定的className,可以优化一下,在生成时,使用随机class名,销毁时不会相互影响
          var kalert = document.getElementsByClassName('kalert-mask');
          for(let i=0; i<kalert.length; i++){
              //删除元素
              if (kalert[i] != null) {
                kalert[i].remove()
              }
          }
          this.afterClose()
        }
      }


      // 插件引用时就向head中插入样式,只加载一次
      var css = (function css() {
        var head = document.head || document.getElementsByTagName('head')[0];
        var style = document.createElement('style');
        style.innerHTML = '.kalert-mask {display: none;} .kalert-show .kalert-mask{ display: flex;align-items: center;justify-content: center; position: fixed;left: 0;top: 0;z-index:999;height: 100%;width: 100%;background: rgba(0,0,0,0.75);} .kalert-wrapper{ width: 75%;display: flex; justify-content: center; } .kalert-box{padding: 0;background: #fff;border-radius: 15px;max-width: 750px ;width: 100%;} .kalert-title{margin: 0;padding: 10px 10px 5px 10px; color: #333;font-size: 16px;text-align:center;}.kalert-content{padding: 5px 10px 10px 10px; color: #666;font-size: 14px;text-align: center;} .kalert-footer{border-top: 1px solid #eee;padding: 0;display: flex; align-items: center;justify-content: space-around;} .kalert-footer button{font-size: 12px;border: 0;border-radius: 0;box-shadow: none;background: transparent;display: block;width: 50%;padding: 10px 0;}.kalert-footer .kalert-cancel-btn{ border-right: 1px solid #eee; }';
        head.appendChild(style);
      })();


     // 最后将插件对象暴露给全局对象
      _global = (function(){ return this || (0, eval)('this'); }());
      if (typeof module !== "undefined" && module.exports) {
          module.exports = KAlert;
      } else if (typeof define === "function" && define.amd) {
          define(function(){return KAlert;});
      } else {
          !('KAlert' in _global) && (_global.KAlert = KAlert);
      }
    }());

插件使用:

<button onclick="customClick()">显示弹出框</button>
<script>
    var customClick = function() {
      // 参数1:弹框标题
      // 参数2:弹框内容
      // 参数3:自定义配置
      // 参数4:关闭前的回调函数
      // 参数5:关闭后的回调函数
      KAlert('提示', '登录信息已过期, 请重新登录', {},
        function () { // 关闭前的回调
          console.log('关闭前的回调')
        },
        function () { // 关闭后的回调
          console.log('关闭后的回调')
        }
      );
    }
</script>

 

更新于:2020-07-22 10:36:36
返回顶部