Bootstrap模态窗口源码解析


Posted in Javascript onFebruary 08, 2017

前言:
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
 // ======================

 var Modal = 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.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) {
 return this.isShown ? this.hide() : this.show(_relatedTarget)
 }

 Modal.prototype.show = function (_relatedTarget) {
 var that = this
 var e = $.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 () {//遮罩层:真的是一个压轴函数,,,,   这个回调函数是遮罩完毕后运行的函数
  var transition = $.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);

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
一个可拖拽列宽表格实例演示
Nov 26 Javascript
node.js中的console用法总结
Dec 15 Javascript
javascript实现依次输入input自动定焦
Dec 23 Javascript
JS实现完全语义化的网页选项卡效果代码
Sep 15 Javascript
jQuery实现切换页面过渡动画效果
Oct 29 Javascript
Jquery Ajax Error 调试错误的技巧
Nov 20 Javascript
探究Javascript模板引擎mustache.js使用方法
Jan 26 Javascript
JS实现同一DOM元素上onClick事件与onDblClick事件并存的解决方法
Jun 07 Javascript
微信小程序scroll-view仿拼多多横向滑动滚动条
Apr 21 Javascript
JS实现百度网盘任意文件强制下载功能
Aug 31 Javascript
jquery.pager.js实现分页效果
Jul 29 jQuery
Js数组扁平化实现方法代码总汇
Nov 11 Javascript
Bootstrap路径导航与分页学习使用
Feb 08 #Javascript
Bootstrap缩略图与警告框学习使用
Feb 08 #Javascript
Bootstrap导航条学习使用(二)
Feb 08 #Javascript
jquery对象与DOM对象转化
Feb 08 #Javascript
vue2 如何实现div contenteditable=“true”(类似于v-model)的效果
Feb 08 #Javascript
jQuery中Datatables增加跳转到指定页功能
Feb 08 #Javascript
node.js的事件机制
Feb 08 #Javascript
You might like
PHP array_push 数组函数
2009/12/26 PHP
浅谈web上存漏洞及原理分析、防范方法(文件名检测漏洞)
2013/06/29 PHP
Yii2中事务的使用实例代码详解
2016/09/07 PHP
详解PHP原生DOM对象操作XML的方法
2016/10/17 PHP
js实现一个省市区三级联动选择框代码分享
2013/03/06 Javascript
通过JQuery实现win8一样酷炫的动态磁贴效果(示例代码)
2013/07/13 Javascript
js定时调用方法成功后并停止调用示例
2014/04/08 Javascript
JavaScript设置获取和设置属性的方法
2015/03/04 Javascript
详细分析JavaScript函数定义
2015/07/16 Javascript
jQuery实现的给图片点赞+1动画效果(附在线演示及demo源码下载)
2015/12/31 Javascript
JavaScript+CSS实现的可折叠二级菜单实例
2016/02/29 Javascript
bootstrap配合Masonry插件实现瀑布式布局
2017/01/18 Javascript
浅谈Javascript事件对象
2017/02/05 Javascript
JS使用H5实现图片预览功能
2019/09/30 Javascript
vue 实现购物车总价计算
2019/11/06 Javascript
Vue3不支持Filters过滤器的问题
2020/09/24 Javascript
ssh批量登录并执行命令的python实现代码
2012/05/25 Python
Python在线运行代码助手
2016/07/15 Python
浅谈python中列表、字符串、字典的常用操作
2017/09/19 Python
python实现神经网络感知器算法
2017/12/20 Python
python调用并链接MATLAB脚本详解
2019/07/05 Python
wxpython自定义下拉列表框过程图解
2020/02/14 Python
python生成任意频率正弦波方式
2020/02/25 Python
python实现FTP文件传输的方法(服务器端和客户端)
2020/03/20 Python
canvas里面如何基于随机点绘制一个多边形的方法
2018/06/13 HTML / CSS
美国摄影爱好者购物网站:Focus Camera
2016/10/21 全球购物
I.T集团香港官方商城:ITeSHOP.com Hong Kong
2019/02/15 全球购物
阿迪达斯希腊官方网上商店:adidas希腊
2019/04/06 全球购物
优秀导游先进事迹材料
2014/01/25 职场文书
关于读书的演讲稿400字
2014/08/27 职场文书
2014审计局领导班子民主生活会对照检查材料思想汇报
2014/09/20 职场文书
六一儿童节新闻稿
2015/07/17 职场文书
简单聊聊Vue中的计算属性和属性侦听
2021/10/05 Vue.js
36个正则表达式(开发效率提高80%)
2021/11/17 Javascript
MySQL学习必备条件查询数据
2022/03/25 MySQL
pytorch分类模型绘制混淆矩阵以及可视化详解
2022/04/07 Python