一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

Bootstrap模态窗口源码解析

时间:2022-06-25 18:07:58 编辑:袖梨 来源:一聚教程网

bootstrap的 js插件的源码写的非常好,也算是编写jquery插件的模范写法,本来还想大篇详细的分析一下呢,唉,没时间啊,很早之前看过的源码了,现在贴在了博客上,

300来行的代码,其中有很多jquery的高级用法,建议,从github上下载一下源码,然后把本篇的代码复制过去,然后,边运行,边阅读,如果有不明白的地方,可以给我留言,我给解答。

下面是基本每行都加了注释,供大家参考,具体内容如下

 

 代码如下复制代码

/* ========================================================================

 * Bootstrap: modal.js v3.3.7

 * http://getbootstrap.com/javascript/#modals

 * ========================================================================

 * Copyright 2011-2016 Twitter, Inc.

 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)

 * ======================================================================== */

 

 

+function($) {

 'use strict'

 

 // MODAL CLASS DEFINITION

 // ======================

 

 varModal =function(element, options) {//modal类:首先是Modal的构造函数,里面声明了需要用到的变量,随后又设置了一些常量。

 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//忽略遮罩成点击吗,不忽略,即:点击遮罩层退出模态

 

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

 

 Modal.TRANSITION_DURATION = 300//transition duration 过度时间

 Modal.BACKDROP_TRANSITION_DURATION = 150 //背景过度时间

 

 Modal.DEFAULTS = {//defaults 默认值

 backdrop:true,//有无遮罩层

 keyboard:true,//键盘上的 esc 键被按下时关闭模态框。

 show:true//模态框初始化之后就立即显示出来。

 }

//变量设置完毕,接着就该上函数了。Modal的扩展函数有这么几个:

//toggel,show,hide,enforceFocus,escape,resize,hideModal,removeBackdrop,

//backdrop,handleUpdate,adjustDialog,resetAdjustments,checkScrollbar,setScrollbar,resetScrollbar,

//measureScrollbar终于列完了,恩一共是16个。toggel函数比较简单,是一个显示和隐藏的切换函数。代码如下

 Modal.prototype.toggle =function(_relatedTarget) {

 returnthis.isShown ?this.hide() :this.show(_relatedTarget)

 }

 

 Modal.prototype.show =function(_relatedTarget) {

 varthat =this

 vare = $.Event('show.bs.modal', { relatedTarget: _relatedTarget })//触发 尾行注册的show.bs.modal事件,并给relatedTarget赋值 $.Event 创建事件对象的目的就是可以给他任意赋值

 

 this.$element.trigger(e)//

 

 if(this.isShown || e.isDefaultPrevented())return//如果已经显示就返回

 

 this.isShown =true//标记

 

 this.checkScrollbar()//核对是否有滚动条,并且测量滚动条宽度(非x轴)

 this.setScrollbar()//如果有滚动条,就给body一个padding-right 是一个滚动条的宽度,这一步的目的是为了呼应,下面的去掉y轴滚动条

 this.$body.addClass('modal-open')//接着为body元素添加modal-open类、即去掉y轴滚动条,页面就会往右一个滚动条的宽度,

 

 this.escape()//按esc退出模态

 this.resize()//窗口大小调整,窗口大小改变,模态框也跟着变

 

 this.$element.on('click.dismiss.bs.modal','[data-dismiss="modal"]', $.proxy(this.hide,this))//注册右上角关闭事件

 

 this.$dialog.on('mousedown.dismiss.bs.modal',function() {//在dialog中按下鼠标,在父元素中抬起   忽略: 在模态中按下鼠标,在遮罩层中抬起鼠标

  that.$element.one('mouseup.dismiss.bs.modal',function(e) {//在父元素中抬起

  if($(e.target).is(that.$element)) that.ignoreBackdropClick =true

  })

 })

 

 this.backdrop(function() {//遮罩层:真的是一个压轴函数,,,,   这个回调函数是遮罩完毕后运行的函数

  vartransition = $.support.transition && that.$element.hasClass('fade')

 

  if(!that.$element.parent().length) {

  that.$element.appendTo(that.$body)// don't move modals dom position

  }

 

  that.$element

  .show()

  .scrollTop(0)//show 并且 弄到顶部

 

  that.adjustDialog()//调整对话框

 

  if(transition) {

  that.$element[0].offsetWidth// force reflow

  }

 

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

   that.$element.trigger('focus').trigger(e)//模态过度完成后,触发foucus 和shown.bs.modal

   })

   .emulateTransitionEnd(Modal.TRANSITION_DURATION) :

  that.$element.trigger('focus').trigger(e)//否则直接进行

 })

 }

 

 Modal.prototype.hide = function (e) {//他的存在就是一个事件监听函数,所以可以加e 下面是模态窗口关闭处理函数

 if (e) e.preventDefault()//取消默认行为

 

 e = $.Event('hide.bs.modal')//无论什么事件进入这里都换成 'hide.bs.modal' 妈的这样就通用了,,,无论是点击“x”,还是取消,确定,都这么处理,

 

 this.$element.trigger(e)//处发hide

 

 if (!this.isShown || e.isDefaultPrevented()) return

 

 this.isShown = false//恢复初始的false

 

 this.escape()//移除esc事件

 this.resize()//移除为window绑定的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))//到了这里,虽然模态已经没有了,但仅仅是把透明度改为0了,this.hideModal才是真正移除

  .emulateTransitionEnd(Modal.TRANSITION_DURATION) :

  this.hideModal()

 }

 

 Modal.prototype.enforceFocus = function () {//模态框获得焦点

 $(document)

  .off('focusin.bs.modal') // guard against infinite focus loop

  .on('focusin.bs.modal', $.proxy(function (e) {

  if (document !== e.target &&

   this.$element[0] !== e.target &&

   !this.$element.has(e.target).length) {

   this.$element.trigger('focus')

  }

  }, this))

 }

 

 Modal.prototype.escape = function () {//键盘上的 esc 键被按下时关闭模态框。

 if (this.isShown && this.options.keyboard) {//仅在模态窗显示的时候才注册这个事件

  this.$element.on('keydown.dismiss.bs.modal', $.proxy(function (e) {//不仅可以把事件穿过来,。。。牛

  e.which == 27 && this.hide()//27 时调用hide方法(ps:比if省事多了,高手就是高手)

  }, this))

 } else if (!this.isShown) {//否则移除他,感觉怪怪的,不管了

  this.$element.off('keydown.dismiss.bs.modal')

 }

 }

 

 Modal.prototype.resize = function () {//为你window resize注册事件

 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

 }

 

 Modal.prototype.backdrop = function (callback) {

 var that = this

 var animate = this.$element.hasClass('fade') ? 'fade' : ''//是否有fade动画类

 

 if (this.isShown && this.options.backdrop) {//条件:正在show,并且有遮罩层

  var doAnimate = $.support.transition && animate// do的条件是支持css3过度和有fade class

 

  this.$backdrop = $(document.createElement('div'))//创建遮罩层div

  .addClass('modal-backdrop' + animate)//添加 modal-backdrop 和fade class

  .appendTo(this.$body)//加到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')//添加in 0.5的透明度

 

  if (!callback) return

 

  doAnimate ?

  this.$backdrop

   .one('bsTransitionEnd', callback)

   .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) ://如果有fade动画的话 就给遮罩层绑定一个遮罩过度时间,为什么要这么写以后聊

  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 () {//用来为body元素设置padding-right的值,防止body的元素被scrollbar阻挡

 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 测量 Scrollbar

 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)))//把modal对象存起来,避免第二次打开时在new对象,这点值得学习

  if (typeof option == 'string') data[option](_relatedTarget)/*这里是区分option是对象和字符串的情况*/

  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 这里是不用一行js代码就实现modal的关键

 // ==============

 

 $(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())//这地方是区别一下第一次触发和第二次触发

  //到这里为止是为了得到被控制的modal的dom元素

 if ($this.is('a')) e.preventDefault()

 

 $target.one('show.bs.modal', function (showEvent) {//调用show方法后立即执行的事件

  if (showEvent.isDefaultPrevented()) return // only register focus restorer if modal will actually get shown

  $target.one('hidden.bs.modal', function () {//调用show后创建的事件,模态框隐藏后触发,

  $this.is(':visible') && $this.trigger('focus')//如果原来的按钮还存在的(或显示的)话,那就让他得到焦点

  })

 })

 Plugin.call($target, option,this)

 })

 

}(jQuery);

 

热门栏目