Bootstrap.js (1)-- modal
来源:互联网 发布:美国 二战 知乎 编辑:程序博客网 时间:2024/05/16 09:08
使用
<button type="button" class="btn" data-toggle="modal" data-target="#myModal"> 触发</button> <div class="modal" id="myModal"> <div class="modal-content"> ... </div> </div>
Tips
if (typeof jQuery === 'undefined') {// 判断是否引入jq throw new Error('Bootstrap\'s JavaScript requires jQuery')}/* ‘+function’ ‘!function’ ‘-function’ 等价于(function(){})(), 自执行函数。把函数声明式变成了函数表达式 */+function ($) { // 判断jq版本号 'use strict'; var version = $.fn.jquery.split(' ')[0].split('.') if ((version[0] < 2 && version[1] < 9) || (version[0] == 1 && version[1] == 9 && version[2] < 1) || (version[0] > 3)) { throw new Error('Bootstrap\'s JavaScript requires jQuery version 1.9.1 or higher, but lower than version 4') }}(jQuery);
原理 参考引用
- 页面完成加载后,为所有含有data-toggle=”modal”的元素绑定点击事件
- 模态框出现的逻辑:先出现遮罩层,再出现模态框
- 为body增加”modal-open”类,作用是给body增加overflow:hidden隐藏页面的滚动条。给模态框增加overflow-x:hidden;overflow-y:auto美化样式
- 调用dropback函数,变量shown为true,所以插入出现遮罩层(若遮罩层需要过渡,过渡的逻辑也在这里)
- 在传给dropback的回调里(即完成遮罩层出现后),调用$el.show()方法使模态框出现(display:block),再增加”in”类使他过渡的出现
- 模态框的隐藏逻辑:先隐藏模态框,再隐藏遮罩层
- 先去除”in”类,使模态框opacity变为0
- 在过渡效果的完成后用$el.hide()隐藏模态框
- 调用dropback函数,变量shown为false,所以移除遮罩层
- 在出给dropback的回调里(即完成遮罩层的隐藏后),清除”modal-open”类
Modal
我先看的后面的参数设置,以及获得并处理参数
// MODAL PLUGIN DEFINITION // ======================= // 有一些看不懂,猜测主要是对设置的参数进行处理 function Plugin(option, _relatedTarget) { return this.each(function () { var $this = $(this) var data = $this.data('bs.modal') // 合并配置参数$.extend,用自定义参数合并Modal.DEFAULTS var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option) // 如果没有参数则创建新的modal对象,使用默认模态框 if (!data) $this.data('bs.modal', (data = new Modal(this, options))) // 如果参数是字符,'toggle','show','hide' 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 // ============== // 为所有具有 data-toggle属性的对象元素绑定Modal事件 // - on(events,[selector],[data],fn) ,(绑定的事件,过滤触发事件的元素,事件触发时传入的值,回调),这里就是为拥有'[data-toggle="modal"]'的元素绑定'click.bs.modal.data-api'事件 $(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) { /* - href||$target : data-target="#foo"或者 href="#foo" 属性,用于指向被控制的模态框。 - href.replace(/.*(?=#[^\s]+$)/, ''),匹配#开始,非空白字符结尾之前的位置,用' '替换 */ var $this = $(this) var href = $this.attr('href') // $target是需要打开的modal层的id值 var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) // strip for ie7 // option是指一些属性,比如是否开启鼠标控制等 var option = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data()) /* - $target.one('show.bs.modal'),绑定一次性的函数 - showEvent.isDefaultPrevented()事件对象中是否调用过 event.preventDefault() 方法来返回一个布尔值。 */ if ($this.is('a')) e.preventDefault() // 阻止a标签的默认跳转 $target.one('show.bs.modal', function (showEvent) { // 这个可能为了防止多次点击 if (showEvent.isDefaultPrevented()) return // only register focus restorer if modal will actually get shown // 为modal层绑定一次性关闭事件,如果当前modal层可见,那就触发modal层的focus事件 $target.one('hidden.bs.modal', function () { $this.is(':visible') && $this.trigger('focus') }) }) // 需要显示的modal层对象,相关设置,以及当前点击的按钮对象 Plugin.call($target, option, this) })}(jQuery);
Modal构造函数
this.options = options this.$body = $(document.body) this.$element = $(element) // modal层对象 this.$dialog = this.$element.find('.modal-dialog') // modal层对象子元素中的.modal-dialog,表示弹出的内容框 this.$backdrop = null // 静态的背景,当用户点击模态框外部时不会关闭模态框。 this.isShown = null // 是否展示状态 this.originalBodyPad = null // body原本的paddingRight,用于重置 this.scrollbarWidth = 0 // 滚动条的宽度 this.ignoreBackdropClick = false // 是否正在执行动画 // 从指定的url中load内容 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 Modal.BACKDROP_TRANSITION_DURATION = 150 Modal.DEFAULTS = { // 默认属性 backdrop: true, // 开启黑色遮罩,可以点击黑色部分关闭模态框 keyboard: true, // 可以通过esc控制模态框的退出 show: true // 初始化时显示模态框,false为需要点击按钮触发才显示 } // toggle,如果是展示状态则关闭,如果是关闭状态则开启 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事件 this.$element.trigger(e) // 触发show.bs.modal事件,模态框打开 if (this.isShown || e.isDefaultPrevented()) return // 已经显示了就不处理,防止多次点击 this.isShown = true // 更改显示状态为显示 this.checkScrollbar() // 检查是否有滚动条出现,并且用插入div的方式模拟出如果有滚动条,那么滚动条的宽度是多少 this.setScrollbar() // 如果有滚动条出现,则设置body的padding-right为原本的值+滚动条的值 this.$body.addClass('modal-open') // 隐藏body的滚动条,模态框设置overflow:auto this.escape() // 绑定esc退出事件 this.resize() // 判断modal层是否overflow,需要改变padding值 // data-dismiss="modal"给按钮添加,则可以关闭modal // 给data-dismiss="modal"属性的按钮绑定hide方法,关闭modal this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this)) // modal层内容中鼠标按下的时候,绑定一次性的鼠标松开事件,ignoreBackdropClick = true,即表示正在执行动画 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 }) }) // modal层的展示逻辑,以及transition效果实现 this.backdrop(function () { // 在完成遮罩层的展示后,调用如下方法调用遮罩层中的内容曾的展示 // $.support.transition 浏览器支持transition特效,并且modal层具有fade类 var transition = $.support.transition && that.$element.hasClass('fade') // 务必将模态框的 HTML 代码放在文档的最高层级内(也就是说,尽量作为 body 标签的直接子元素),以避免其他组件影响模态框的展现和/或功能。(官网) if (!that.$element.parent().length) { // 如果不在最外层,也要把它放到最外层去 that.$element.appendTo(that.$body) // don't move modals dom position } that.$element .show() .scrollTop(0) // jq方法,模态框出现以及设置相对滚动条顶部的偏移为0 that.adjustDialog() // 这里判断modal有overflow,上面明明判断了啊...不造了 if (transition) { that.$element[0].offsetWidth // force reflow } that.$element.addClass('in') // 添加in类,filter: alpha(opacity=50);opacity: .5; 产生动画效果 that.enforceFocus() // enforceFocus就是强制获取焦点的意思,我的理解是内容框内有input输入框,那么给他获取焦点 var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget }) // 动画存在,就完成后续的focus 调用transition插件的动画特效,如果不存在,就直接调用focus transition ? that.$dialog // wait for modal to slide in // 给模态框内容框绑定一次性,transition动画结束事件 .one('bsTransitionEnd', function () { that.$element.trigger('focus').trigger(e) }) // .emulateTransitionEnd(Modal.TRANSITION_DURATION) 是transition插件里面的方法 .emulateTransitionEnd(Modal.TRANSITION_DURATION) : that.$element.trigger('focus').trigger(e) }) } Modal.prototype.hide = function (e) { if (e) e.preventDefault() e = $.Event('hide.bs.modal') // 注册hide.bs.modal事件 this.$element.trigger(e) // 激活事件 if (!this.isShown || e.isDefaultPrevented()) return // 如果modal没有展示,则不处理 this.isShown = false // 关闭modal,设置isShown为false // 取消在show的时候的事件绑定 this.escape() this.resize() $(document).off('focusin.bs.modal') // 取消'focusin.bs.modal'事件绑定,应该是不再聚焦modal // 移除in类,取消.off('click.dismiss.bs.modal').off('mouseup.dismiss.bs.modal')绑定 this.$element .removeClass('in') .off('click.dismiss.bs.modal') .off('mouseup.dismiss.bs.modal') this.$dialog.off('mousedown.dismiss.bs.modal') // 如果支持transition属性,并且modal层有fade类执行动画隐藏效果,否则直接隐藏 $.support.transition && this.$element.hasClass('fade') ? this.$element .one('bsTransitionEnd', $.proxy(this.hideModal, this)) .emulateTransitionEnd(Modal.TRANSITION_DURATION) : this.hideModal() } // 强制对内容框聚焦,比如内容框内有input输入框,则聚焦(猜测) Modal.prototype.enforceFocus = function () { $(document) // 解除了当前文档的focusin事件,原因作者也进行了解释,防止无线聚焦循环 .off('focusin.bs.modal') // guard against infinite focus loop .on('focusin.bs.modal', $.proxy(function (e) { // 使用proxy,改变this,否则指向document if (document !== e.target && 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) {// modal层已经展示,并且设置了keyboard参数 // 绑定keydown.dismiss.bs.modal事件, // e.which表示按住的是哪一个按键 // 如果按了esc键,那么调用this.hide() 方法 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层是否overflow Modal.prototype.resize = function () { if (this.isShown) { // 如果modal展示,那就触发resize.bs.modal事件,并且判断modal层是否overflow,改变padding值 $(window).on('resize.bs.modal', $.proxy(this.handleUpdate, this)) } else { // 如果没有展示那么取消resize.bs.modal绑定 $(window).off('resize.bs.modal') } } // 隐藏模态框 Modal.prototype.hideModal = function () { var that = this this.$element.hide() this.backdrop(function () { // backdrop回调 // 完成遮罩层的隐藏之后,调用如下的方法,重置 that.$body.removeClass('modal-open') // 重置body,modal层的padding值 that.resetAdjustments() that.resetScrollbar() that.$element.trigger('hidden.bs.modal') }) } // 移除黑色遮罩层 Modal.prototype.removeBackdrop = function () { this.$backdrop && this.$backdrop.remove() this.$backdrop = null } // 展示 隐藏modal回调函数 包含了动画的逻辑效果 Modal.prototype.backdrop = function (callback) { var that = this var animate = this.$element.hasClass('fade') ? 'fade' : '' // 当前弹出框显示,并且有黑色遮罩层的时候 // 此事是需要弹出内容层 if (this.isShown && this.options.backdrop) { // 支持transition 和animate动画效果 var doAnimate = $.support.transition && animate // 插入有mmodal-backdrop-animate类的div,我猜测应该是黑的遮罩层 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) { // 不需要点击背景modal消失,直接返回 this.ignoreBackdropClick = false // 标志动画结束 return } // e.target !== e.currentTarget // target返回的是触发事件的元素,currentTarget返回的是绑定事件的元素,比如事件绑定在ul上,点击ul的li标签,target返回li,二current返回ul 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 // 添加in类 .fade.in { opacity: 1;} 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') // opacity = 0 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层是否有滚动条,设置modal层的padding值 Modal.prototype.adjustDialog = function () { var modalIsOverflowing = this.$element[0].scrollHeight > document.documentElement.clientHeight // modal层是否有滚动区 this.$element.css({ paddingLeft: !this.bodyIsOverflowing && modalIsOverflowing ? this.scrollbarWidth : '', paddingRight: this.bodyIsOverflowing && !modalIsOverflowing ? this.scrollbarWidth : '' }) } // 重置modal层的padding值 Modal.prototype.resetAdjustments = function () { this.$element.css({ paddingLeft: '', paddingRight: '' }) } Modal.prototype.checkScrollbar = function () { var fullWindowWidth = window.innerWidth // 窗口大小,包含滚动条的整个窗口 ie8以下不兼容 if (!fullWindowWidth) { // workaround for missing window.innerWidth in IE8 // documentElement 属性可返回文档的根节点 // getBoundingClientRect()返回一个元素到页面上下左右的边距 // 用右边的距离-左边的距离虽然我理解意思,可是不是很理解为什么这个值是这个值 var documentElementRect = document.documentElement.getBoundingClientRect() fullWindowWidth = documentElementRect.right - Math.abs(documentElementRect.left) } // clientWidth不包括滚动条,如果可视区的大小 < 整个窗口,说明出现了滚动条 // scrollbarWidth获得滚动条的宽度 this.bodyIsOverflowing = document.body.clientWidth < fullWindowWidth this.scrollbarWidth = this.measureScrollbar() } // 修改body的右内边距为原本的值,加上滚动条的值 Modal.prototype.setScrollbar = function () { var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10) this.originalBodyPad = document.body.style.paddingRight || '' // 如果有滚动条,那么设置body的padding-right的值为原来的值加上滚动条的值 if (this.bodyIsOverflowing) this.$body.css('padding-right', bodyPad + this.scrollbarWidth) } Modal.prototype.resetScrollbar = function () { this.$body.css('padding-right', this.originalBodyPad) // 重置body的paddingRight的值 } // 测出滚动条的宽度 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 }
总结
总的来说,我觉得bootstrap属于一个事件驱动的框架,比如e = $.Event(‘hide.bs.modal’)注册一个事件,然后给很多情况下都绑定这个事件,比如点击按钮就绑定这个事件,就像js原生里的自定义函数,
我觉得这样可以让代码很有条理性吧,同样是点击事件,但是就区分了不同情况下的点击事件代码很复杂,但是他暴露的接口或者说使用方式很简单,不需要用户写任何js基本就可以实现效果,这里也就用到了用户控制类,我们控制逻辑实现
这个组件,还有一个优点就是没有特别大面积的使用css的属性,就是没有直接操作css,基本都是通过类,还有纯js逻辑来控制,做到了样式和逻辑的分离这个很棒
缺点,就是我觉得似乎写的有点复杂了,当然了,目前,或者一年之内,感觉自己是想复杂都没有办法。哈哈哈哈哈哈 其实这个代码读一遍基本是无法理解到他的还有90&的精髓,只能大概读懂意思,对他的构架其实还是不是很清楚,但是我能感觉到,他写的有点好。哈哈哈
0 0
- Bootstrap.js (1)-- modal
- bootstrap modal.js解析
- Bootstrap的js插件之模态框(modal)
- Bootstrap modal
- Bootstrap-Modal
- BootStrap Modal
- bootstrap modal
- modal (Bootstrap)
- 十二.通过bootstrap的modal.js来完成删除功能
- bootstrap——js插件(一、modal)
- JQuery或者Js函数显示Bootstrap模态框(modal)
- 关于使用bootstrap中modal和ajaxupload.js图片问题
- js bootstrap modal 点击空白 不自动关闭
- IE8-11,bootstrap-modal.js,模态框,缓存问题
- Bootstrap插件(一)——模态框(modal.js)
- 【bootstrap】 bootstrap-modal: 点击遮罩层不关闭modal
- bootstrap modal弹出框
- Bootstrap迁移系列 - Modal
- 关于编译报错“dereferencing pointer to incomplete type
- linux下的粘滞位
- TortoiseGit 远程上传push代码,配置不用每次输入用户名和密码方法
- oracle查询表空间下的所有用户
- codeforces 414C 归并排序
- Bootstrap.js (1)-- modal
- 仿Android5.0 Heads-Up风格的Toast提示
- IO流加强(总结)--IO流总结和练习
- 由于出现以下异常,无法生成模型:“System.Data.StrongTypingException: 表“TableDetails”中列“IsPrimaryKey”的值为 DBNull
- A. (网预)Saber's Conjecture 第十一届北京邮电大学程序设计竞赛
- 数据结构—树和二叉树-二叉树的递归遍历(DFS)
- sphinx 增量索引实现实例
- 计蒜客模拟赛5-礼物盒
- oj刷题—Problem D: C++习题-快速排序