赞
踩
两部分需要定义:
data-toggle="modal"
,以便被初始化;需要data-target="#example"
或href
指定模态框data-target
对应的类或id
,其内容部分可以用"modal-content
"的类来美化样式 <button type="button" class="btn" data-toggle="modal" data-target="#myModal"> 触发按钮 </button>
<div class="modal" id="myModal">
<div class="modal-content">
<div class="modal-header">
title
</div>
<div class="modal-body">
body
</div>
</div>
</div>
页面完成加载后,为所有含有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
先去除”in
"类,使模态框opacity
变为0
$el.hide()
隐藏模态框 调用dropback
函数,变量shown
为false
,所以移除遮罩层dropback
的回调里(即完成遮罩层的隐藏后),清除"modal-open
"类为触发元素绑定点击事件
// 在初始化时为含data-toggle属性的元素绑定点击事件 $(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') }) }) // 把对应的模态框的juqery对象作为this,传入Plugin中 // 把参数和触发元素作为参数参数Plugin中 Plugin.call($target, option, this) })
模态框的核心是
show()
检查并绑定键盘、调整大小的事件,然后调用backdrop()
使遮罩层和模态框顺序出现hide()
接触事件绑定,然后调用hideModal()
隐藏模态框,移除遮罩层但是为了考虑全面。Bootstrap
做了太多情况的考虑,导致代码变得很复杂。。。
默认在这里调用了show
方法
/** * 取触发元素上的data(),获得对应参数并实例化Modal * @param {object} option 触发元素的缓存数据对象,或字符串直接调用方法 * @param {object} _relatedTarget 触发元素的dom对象 */ 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) }) }
/** * 初始化实例属性,如有有remote则从远端加载内容 * @param {object} element 模态框的dom对象 * @param {object} options 参数 */ var Modal = function (element, options) { this.options = options this.$body = $(document.body) this.$element = $(element) // 模态框的jquery对象 this.$dialog = this.$element.find('.modal-dialog') this.$backdrop = null this.isShown = null this.originalBodyPad = null 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)) } }
show
方法/** * 完成滚动条的检查,键盘事件,窗口大小事件的绑定。在backdrop遮罩层出现后,才在回调里显示模态框 * 顺序是checkScrollbar->setScrollbar->escape->resize->backdrop->模态框显示 * @param {object} _relatedTarget 触发元素的dom对象 * @return {undefined} 无返回值 */ 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() 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 () { var transition = $.support.transition && that.$element.hasClass('fade') if (!that.$element.parent().length) { that.$element.appendTo(that.$body) // don't move modals dom position } // 这里scrollTop(0)干嘛? that.$element .show() .scrollTop(0) 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) }) .emulateTransitionEnd(Modal.TRANSITION_DURATION) : that.$element.trigger('focus').trigger(e) }) }
hide
方法/** 与show相反,先取消一系列事件,在调用hideModal隐藏模态框,然后移除遮罩层 */ 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 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() }
backdrop
方法:负责遮罩层的出现与消失逻辑/** * 负责模态框出现后,增加遮罩层,及模态框消失时取出遮罩层的逻辑 * @param {Function} callback 遮罩层出现后,及消失后的回调 * @return {undefined} 无 */ Modal.prototype.backdrop = function (callback) { var that = this var animate = this.$element.hasClass('fade') ? 'fade' : '' // backdrop默认为true // 如果已经show了 if (this.isShown && this.options.backdrop) { var doAnimate = $.support.transition && animate // 插入遮罩层 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() } }
checkScrollbar
和setScrollbar
方法/** 1. 检查当前是否overflow,并获取滚动条宽度 2. @return {[type]} [description] */ Modal.prototype.checkScrollbar = function () { var fullWindowWidth = window.innerWidth // 窗口大小,包含滚动条的整个窗口 if (!fullWindowWidth) { // workaround for missing window.innerWidth in IE8 var documentElementRect = document.documentElement.getBoundingClientRect() // IE8没有window.innerWidth,所以需要用左边减右边来获得窗口大小 fullWindowWidth = documentElementRect.right - Math.abs(documentElementRect.left) } // 窗口大小是否大于可视窗口大小,来判断body宽度是否overflow。如果大于,则overflow // 窗口大小比可视窗口大小大的值,其实就是滚动条的宽度(不用这个方法获取滚动条宽度是,万一页面没有滚动条,那就无法获取滚动条宽度) this.bodyIsOverflowing = document.body.clientWidth < fullWindowWidth this.scrollbarWidth = this.measureScrollbar() } /** 3. 如果有滚动条,则body需要padding-right,滚动条宽度的值 */ 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) }
window.innerWidth
可以获得包含滚动条的整个窗口的大小(但IE8需用document.documentElement.getBoundingClientRect().left
减去right
)
window.innerWidth
> document.body.clientWidth
来判断是否有滚动条
也可以利用插入div
,然后用el.offsetWidth
- el.clientWidth
来获得当前浏览器中滚动条的宽度
"fade
“类定义了opacity: 0
和transition:opacity .15s linear
而”.fade.in
"类定义了opacity:1
。这样能很好的有解耦动画效果和无效果
在利用addClass('in')
做过度效果前,需要强制重排。BS常用el.offsetWidth
来做强制重排
offsetTop、offsetLeft、offsetWidth、offsetHeight、scrollTop、scrollLeft、scrollWidth、scrollHeight、clientTop、clientLeft、clientWidth、clientHeight、getComputedStyle()
(currentStyle in IE)这些属性时,都会引起重排Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。