zoukankan      html  css  js  c++  java
  • Bootstrap modal.js 源码分析

    /* ========================================================================
     * Bootstrap: modal.js v3.3.5
     * http://getbootstrap.com/javascript/#modals
     * ========================================================================
     * Copyright 2011-2015 Twitter, Inc.
     * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
     * ======================================================================== */
    
    
    +function ($) {
      'use strict';
    
      // MODAL CLASS DEFINITION
      // ======================
    
      var Modal = function (element, options) {// element表示modal弹出框容器及内部元
    //素,options是设置选项
        this.options             = options
        this.$body               = $(document.body)
        this.$element            = $(element)
        this.$dialog             = this.$element.find('.modal-dialog')
        this.$backdrop           = null
        this.isShown             = null
        this.originalBodyPad     = null
        this.scrollbarWidth      = 0
        this.ignoreBackdropClick = false
    
    // 如果设置了remote,就加载remote指定url的内容到modal-content样式的元素内,并触发
    // loaded.bs.modal事件
        if (this.options.remote) {
          this.$element
            .find('.modal-content')
            .load(this.options.remote, $.proxy(function () {
              this.$element.trigger('loaded.bs.modal')
            }, this))
        }
      }
    
      Modal.VERSION  = '3.3.5'
    
      Modal.TRANSITION_DURATION = 300
      Modal.BACKDROP_TRANSITION_DURATION = 150
    
      Modal.DEFAULTS = {
        backdrop: true,// 默认单击弹窗以外的地方时自动关闭弹窗
        keyboard: true,// 默认设置,按Esc键关闭弹窗
        show: true// 默认设置,单击触发元素时打开弹窗
      }
    // 反转弹窗状态
      Modal.prototype.toggle = function (_relatedTarget) {
        return this.isShown ? this.hide() : this.show(_relatedTarget)
      }
    // 打开弹窗
      Modal.prototype.show = function (_relatedTarget) {
        var that = this
        var e    = $.Event('show.bs.modal', { relatedTarget: _relatedTarget })
    // 打开弹窗前,触发事件
        this.$element.trigger(e)
    // 如果已经打开了(或者曾经被阻止过),则退出执行,后续代码不做处理
        if (this.isShown || e.isDefaultPrevented()) return
    
        this.isShown = true
    
        this.checkScrollbar()
        this.setScrollbar()
        this.$body.addClass('modal-open')
    
        this.escape()
        this.resize()
    // 如果单击了元素内的子元素(带有[data-dismiss="modal"]属性),则关闭弹窗
        this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this))
    
        this.$dialog.on('mousedown.dismiss.bs.modal', function () {
          that.$element.one('mouseup.dismiss.bs.modal', function (e) {
            if ($(e.target).is(that.$element)) that.ignoreBackdropClick = true
          })
        })
    
        this.backdrop(function () {
    // 判断浏览器是否支持动画,并且弹窗是否设置了动画过渡效果(是否有fade样式)   
          var transition = $.support.transition && that.$element.hasClass('fade')
    // 如果modal弹窗没有父容器,则将它附加到body上
          if (!that.$element.parent().length) {
            that.$element.appendTo(that.$body) // don't move modals dom position
          }
    // 显示modal弹窗
          that.$element
            .show()
            .scrollTop(0)
    
          that.adjustDialog()
    // 如果支持动画,强制刷新UI现场,重绘弹窗
          if (transition) {
            that.$element[0].offsetWidth // force reflow
          }
    // 给modal弹窗添加in样式,和modal样式一起
          that.$element.addClass('in')
    // 强制给弹窗设定焦点
          that.enforceFocus()
    // 打开弹窗显示后的触发事件
          var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget })
    
          transition ?
            that.$dialog // wait for modal to slide in
              .one('bsTransitionEnd', function () {// 如果支持动画,则动画结束以后给弹窗内的元素设置焦点,并触发shown事件
                that.$element.trigger('focus').trigger(e)
              })// 否则直接设置焦点,并触发shown事
              .emulateTransitionEnd(Modal.TRANSITION_DURATION) :
            that.$element.trigger('focus').trigger(e)
        })
      }
    // 关闭弹窗
      Modal.prototype.hide = function (e) {
        if (e) e.preventDefault()// 先阻止冒泡行为
    // 关闭弹窗前的触发事件
        e = $.Event('hide.bs.modal')
    
        this.$element.trigger(e)
    
        if (!this.isShown || e.isDefaultPrevented()) return
    
        this.isShown = false
    // 处理键盘事件,主要是设置按Esc键的时候是否
    //关闭弹窗
        this.escape()
        this.resize()
    
        $(document).off('focusin.bs.modal')
    
        this.$element
          .removeClass('in')
          .off('click.dismiss.bs.modal')
          .off('mouseup.dismiss.bs.modal')
    
        this.$dialog.off('mousedown.dismiss.bs.modal')
    // 如果支持动画,则动画结束以后再关闭,否则直接关闭
        $.support.transition && this.$element.hasClass('fade') ?
          this.$element
            .one('bsTransitionEnd', $.proxy(this.hideModal, this))
            .emulateTransitionEnd(Modal.TRANSITION_DURATION) :
          this.hideModal()
      }
    // 确保当前打开的弹窗处于焦点状态
      Modal.prototype.enforceFocus = function () {
        $(document)// 禁用所有的focusin事件,防止无限循环
          .off('focusin.bs.modal') // guard against infinite focus loop
          .on('focusin.bs.modal', $.proxy(function (e) {
            if (this.$element[0] !== e.target && !this.$element.has(e.target).length) {
              this.$element.trigger('focus')
            }// 如果处于焦点的元素不是当前元素(或不包含当前元素),则强制给当前元素设置
    //焦点
          }, this))
      }
    // 按Esc键是否退出的处理
      Modal.prototype.escape = function () {
        if (this.isShown && this.options.keyboard) {
          this.$element.on('keydown.dismiss.bs.modal', $.proxy(function (e) {
            e.which == 27 && this.hide()
          }, this))
        } else if (!this.isShown) {// 否则,取消键盘事件检测
          this.$element.off('keydown.dismiss.bs.modal')
        }
      }
    
      Modal.prototype.resize = function () {
        if (this.isShown) {
          $(window).on('resize.bs.modal', $.proxy(this.handleUpdate, this))
        } else {
          $(window).off('resize.bs.modal')
        }
      }
    // 关闭弹窗
      Modal.prototype.hideModal = function () {
        var that = this
        this.$element.hide()
        this.backdrop(function () {
          that.$body.removeClass('modal-open')
          that.resetAdjustments()
          that.resetScrollbar()
          that.$element.trigger('hidden.bs.modal')
        })
      }
    // 删除背景,关闭弹窗时触发
      Modal.prototype.removeBackdrop = function () {
        this.$backdrop && this.$backdrop.remove()// 删除背景元素
        this.$backdrop = null// 设置背景对象为null
      }
    // 添加背景,打开弹窗时触发
      Modal.prototype.backdrop = function (callback) {
        var that = this
        // 是否设置了动画过渡效果,如果是则
    //设置为fade
        var animate = this.$element.hasClass('fade') ? 'fade' : ''
    // 如果是打开状态,并且设置了backdrop参数
        if (this.isShown && this.options.backdrop) {
          var doAnimate = $.support.transition && animate
    
    // 在body上定义背景div元素,并附加fade标识以支持动画
          this.$backdrop = $(document.createElement('div'))
            .addClass('modal-backdrop ' + animate)
            .appendTo(this.$body)
    
          this.$element.on('click.dismiss.bs.modal', $.proxy(function (e) {
            if (this.ignoreBackdropClick) {
              this.ignoreBackdropClick = false
              return
            }
            if (e.target !== e.currentTarget) return
            this.options.backdrop == 'static'
              ? this.$element[0].focus()
              : this.hide()
          }, this))
    
          if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
    
          this.$backdrop.addClass('in')
    
          if (!callback) return
    
          doAnimate ?
            this.$backdrop
              .one('bsTransitionEnd', callback)
              .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
            callback()
    
        } else if (!this.isShown && this.$backdrop) {
          this.$backdrop.removeClass('in')
    
          var callbackRemove = function () {
            that.removeBackdrop()
            callback && callback()
          }
          $.support.transition && this.$element.hasClass('fade') ?
            this.$backdrop
              .one('bsTransitionEnd', callbackRemove)
              .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
            callbackRemove()
    
        } else if (callback) {
          callback()
        }
      }
    
      // these following methods are used to handle overflowing modals
    
      Modal.prototype.handleUpdate = function () {
        this.adjustDialog()
      }
    
      Modal.prototype.adjustDialog = function () {
        var modalIsOverflowing = this.$element[0].scrollHeight > document.documentElement.clientHeight
    
        this.$element.css({
          paddingLeft:  !this.bodyIsOverflowing && modalIsOverflowing ? this.scrollbarWidth : '',
          paddingRight: this.bodyIsOverflowing && !modalIsOverflowing ? this.scrollbarWidth : ''
        })
      }
    
      Modal.prototype.resetAdjustments = function () {
        this.$element.css({
          paddingLeft: '',
          paddingRight: ''
        })
      }
    
      Modal.prototype.checkScrollbar = function () {
        var fullWindowWidth = window.innerWidth
        if (!fullWindowWidth) { // workaround for missing window.innerWidth in IE8
          var documentElementRect = document.documentElement.getBoundingClientRect()
          fullWindowWidth = documentElementRect.right - Math.abs(documentElementRect.left)
        }
        this.bodyIsOverflowing = document.body.clientWidth < fullWindowWidth
        this.scrollbarWidth = this.measureScrollbar()
      }
    
      Modal.prototype.setScrollbar = function () {
        var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10)
        this.originalBodyPad = document.body.style.paddingRight || ''
        if (this.bodyIsOverflowing) this.$body.css('padding-right', bodyPad + this.scrollbarWidth)
      }
    
      Modal.prototype.resetScrollbar = function () {
        this.$body.css('padding-right', this.originalBodyPad)
      }
    
      Modal.prototype.measureScrollbar = function () { // thx walsh
        var scrollDiv = document.createElement('div')
        scrollDiv.className = 'modal-scrollbar-measure'
        this.$body.append(scrollDiv)
        var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth
        this.$body[0].removeChild(scrollDiv)
        return scrollbarWidth
      }
    
    
      // MODAL PLUGIN DEFINITION
      // =======================
    
      function Plugin(option, _relatedTarget) {
        return this.each(function () {
          var $this   = $(this)
          var data    = $this.data('bs.modal')
          var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option)
    
          if (!data) $this.data('bs.modal', (data = new Modal(this, options)))
          if (typeof option == 'string') data[option](_relatedTarget)
          else if (options.show) data.show(_relatedTarget)
        })
      }
    
      var old = $.fn.modal
    
      $.fn.modal             = Plugin
      $.fn.modal.Constructor = Modal
    
    
      // MODAL NO CONFLICT
      // =================
    
      $.fn.modal.noConflict = function () {
        $.fn.modal = old
        return this
      }
    
    
      // MODAL DATA-API
      // ==============
    
      $(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) {
        var $this   = $(this)
        var href    = $this.attr('href')
        var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) // strip for ie7
        var option  = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data())
    
        if ($this.is('a')) e.preventDefault()
    
        $target.one('show.bs.modal', function (showEvent) {
          if (showEvent.isDefaultPrevented()) return // only register focus restorer if modal will actually get shown
          $target.one('hidden.bs.modal', function () {
            $this.is(':visible') && $this.trigger('focus')
          })
        })
        Plugin.call($target, option, this)
      })
    
    }(jQuery);
  • 相关阅读:
    为什么大家都说 SELECT * 效率低?
    一个 Java 方法,最多能定义多少参数?
    牛逼哄哄的布隆过滤器,到底有什么用?
    10w+ Excel 数据导入,怎么优化?
    为什么 wait 方法要在 synchronized 中调用?
    使用Redis存储聊天数据的一种方案(使用lua解决原子性问题)
    Linux下安装redis
    Django Rest Framework组件:解析器JSONParser、FormParser、MultiPartParser、FileUploadParser
    API测试之Postman使用全指南(转载)
    Django Rest Framework组件:序列化与反序列化模块Serializer
  • 原文地址:https://www.cnblogs.com/sunBinary/p/10310192.html
Copyright © 2011-2022 走看看