modal.css
html {
font-family: sans-serif;
-webkit-text-size-adjust: 100%;
-ms-text-size-adjust: 100%;
}
body {
margin: 0;
}
article, aside, details, figcaption, figure, footer, header, hgroup, main, menu, nav, section, summary {
display: block;
}
audio, canvas, progress, video {
display: inline-block;
vertical-align: baseline;
}
audio:not([controls]) {
display: none;
height: 0;
}
[hidden],
template {
display: none;
}
a {
background-color: transparent;
}
a:active,
a:hover {
outline: 0;
}
abbr[title] {
border-bottom: 1px dotted;
}
b, strong {
font-weight: bold;
}
dfn {
font-style: italic;
}
h1 {
margin: .67em 0;
font-size: 2em;
}
mark {
color: #000;
background: #ff0;
}
small {
font-size: 80%;
}
sub, sup {
position: relative;
font-size: 75%;
line-height: 0;
vertical-align: baseline;
}
sup {
top: -.5em;
}
sub {
bottom: -.25em;
}
img {
border: 0;
}
svg:not(:root) {
overflow: hidden;
}
figure {
margin: 1em 40px;
}
hr {
height: 0;
-webkit-box-sizing: content-box;
-moz-box-sizing: content-box;
box-sizing: content-box;
}
pre {
overflow: auto;
}
code, kbd, pre, samp {
font-family: monospace, monospace;
font-size: 1em;
}
button, input, optgroup, select, textarea {
margin: 0;
font: inherit;
color: inherit;
}
button {
overflow: visible;
}
button, select {
text-transform: none;
}
button,
html input[type="button"],
input[type="reset"],
input[type="submit"] {
-webkit-appearance: button;
cursor: pointer;
}
button[disabled],
html input[disabled] {
cursor: default;
}
button::-moz-focus-inner,
input::-moz-focus-inner {
padding: 0;
border: 0;
}
input {
line-height: normal;
}
input[type="checkbox"],
input[type="radio"] {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
padding: 0;
}
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
height: auto;
}
input[type="search"] {
-webkit-box-sizing: content-box;
-moz-box-sizing: content-box;
box-sizing: content-box;
-webkit-appearance: textfield;
}
input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
fieldset {
padding: .35em .625em .75em;
margin: 0 2px;
border: 1px solid #c0c0c0;
}
legend {
padding: 0;
border: 0;
}
textarea {
overflow: auto;
}
optgroup {
font-weight: bold;
}
table {
border-spacing: 0;
border-collapse: collapse;
}
td, th {
padding: 0;
}
/**********************************************************/
/**********************************************************/
/**********************************************************/
.fade {
opacity: 0;
-webkit-transition: opacity .15s linear;
-o-transition: opacity .15s linear;
transition: opacity .15s linear;
}
.fade.in {
opacity: 1;
}
.modal-open {
overflow: hidden;
}
.modal {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 1040;
display: none;
overflow: hidden;
-webkit-overflow-scrolling: touch;
outline: 0;
}
.modal.fade .modal-dialog {
-webkit-transition: -webkit-transform .3s ease-out;
-o-transition: -o-transform .3s ease-out;
transition: transform .3s ease-out;
-webkit-transform: translate(0, -25%);
-ms-transform: translate(0, -25%);
-o-transform: translate(0, -25%);
transform: translate(0, -25%);
}
.modal.in .modal-dialog {
-webkit-transform: translate(0, 0);
-ms-transform: translate(0, 0);
-o-transform: translate(0, 0);
transform: translate(0, 0);
}
.modal-open .modal {
overflow-x: hidden;
overflow-y: auto;
}
.modal-dialog {
position: relative;
width: auto;
margin: 10px;
}
.modal-content {
position: relative;
background-color: #fff;
-webkit-background-clip: padding-box;
background-clip: padding-box;
border: 1px solid #999;
border: 1px solid rgba(0, 0, 0, .2);
border-radius: 6px;
outline: 0;
-webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, .5);
box-shadow: 0 3px 9px rgba(0, 0, 0, .5);
}
.modal-backdrop {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
background-color: #000;
}
.modal-backdrop.fade {
filter: alpha(opacity=0);
opacity: 0;
}
.modal-backdrop.in {
filter: alpha(opacity=50);
opacity: .5;
}
.modal-header {
min-height: 16.42857143px;
padding: 15px;
border-bottom: 1px solid #e5e5e5;
}
.modal-header .close {
margin-top: -2px;
}
.modal-title {
margin: 0;
line-height: 1.42857143;
}
.modal-body {
position: relative;
padding: 15px;
}
.modal-footer {
padding: 15px;
text-align: right;
border-top: 1px solid #e5e5e5;
}
.modal-footer .btn + .btn {
margin-bottom: 0;
margin-left: 5px;
}
.modal-footer .btn-group .btn + .btn {
margin-left: -1px;
}
.modal-footer .btn-block + .btn-block {
margin-left: 0;
}
.modal-scrollbar-measure {
position: absolute;
top: -9999px;
width: 50px;
height: 50px;
overflow: scroll;
}
@media (min-width: 768px) {
.modal-dialog {
width: 600px;
margin: 30px auto;
}
.modal-content {
-webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, .5);
box-shadow: 0 5px 15px rgba(0, 0, 0, .5);
}
.modal-sm {
width: 300px;
}
}
@media (min-width: 992px) {
.modal-lg {
width: 900px;
}
}
.modal-footer:before,
.modal-footer:after {
display: table;
content: " ";
}
.modal-footer:after {
clear: both;
}
modal.js
+function ($) {
'use strict';
function transitionEnd() {
var el = document.createElement('bootstrap')
var transEndEventNames = {
WebkitTransition : 'webkitTransitionEnd',
MozTransition : 'transitionend',
OTransition : 'oTransitionEnd otransitionend',
transition : 'transitionend'
}
// el.style.transition
// el.style.WebkitTransition
// el.style.MozTransition
// el.style.OTransition
for (var name in transEndEventNames) {
if (el.style[name] !== undefined) {
return { end: transEndEventNames[name] }
}
}
return false // explicit for ie8 ( ._.)
}
// http://blog.alexmaccaw.com/css-transitions
$.fn.emulateTransitionEnd = function (duration) {
var called = false
var $el = this
$(this).one('bsTransitionEnd', function () { called = true })
var callback = function () { if (!called) $($el).trigger($.support.transition.end) }
setTimeout(callback, duration)
return this
}
$(function () {
$.support.transition = transitionEnd()
if (!$.support.transition) return
$.event.special.bsTransitionEnd = {
bindType: $.support.transition.end,
delegateType: $.support.transition.end,
handle: function (e) {
if ($(e.target).is(this))
return e.handleObj.handler.apply(this, arguments)
}
}
})
}(jQuery);
+(function($){
var Modal = function(element,options){
this.options = options;
this.$body = $(document.body);
this.$element = $(element);
this.$backdrop = null;
this.isShown = null; //模态框是否显示
this.scrollbarWidth = 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.0";
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.scrollbarWidth
this.checkScrollbar();
// 设置overflow: hidden
this.$body.addClass("modal-open");
// 设置body内边距
this.setScrollbar();
// 设置esc按钮事件 调用hide方法
this.escape();
// 为包含data-dismiss="modal"属性的元素注册关闭模态框(比如点x按钮,就隐藏模态框功能)
this.$element.on("click.dismiss.bs.modal","[data-dismiss='modal']",$.proxy(this.hide,this));
//backdrop函数:背景逻辑, 回调函数功能:显示model逻辑
this.backdrop(function(){
// 是否支持动画 && model的元素包含fade class
var transition = $.support.transition && that.$element.hasClass("fade");
// model没有父元素 则将model附加到body上
if(!that.$element.parent().length){
that.$element.appendTo(that.$body)
}
// 将model元素设置成显示(jq.show方法), 并移动到最上面
that.$element.show().scrollTop(0);
// 动画效果准备
if(transition){
that.$element[0].offsetWidth
}
that.$element.addClass("in").attr("aria-hidden",false);
//解绑并为document对象注册focusin.bs.modal事件, 具体处理是:如果不是model产生的,就触发model的facus事件
//简单的说,就是获取焦点
that.enforceFocus();
//准备触发shown.bs.modal事件
var e = $.Event("shown.bs.modal",{ relatedTarget: _relatedTarget});
//有动画,就动画完成后触发focus事件和shown.bs.modal事件
$.support.transition?that.$element.find(".modal-dialog").one("bsTransitionEnd",function(){
that.$element.trigger("focus").trigger(e)
}).emulateTransitionEnd(300):that.$element.trigger("focus").trigger(e);
});
}
// 关闭弹框
Modal.prototype.hide = function(e){
if(!!e) e.preventDefault();
// 绑定hide.bs.modal事件并触发
e = $.Event("hide.bs.modal");
this.$element.trigger(e);
// 如果已经hide了 || 调用hide事件时阻止了默认行为 则返回
if(!this.isShown || e.isDefaultPrevented()) return;
this.isShown = false;
// 解除esc按键事件
this.escape();
// 解除document对象的focusin.bs.modal事件绑定, (对应show中enforceFocus)
$(document).off("focusin.bs.modal");
// 移除class in 解绑click关闭模态框事件
this.$element.removeClass("in").attr("aria-hidden",true).off("click.dismiss.bs.modal");
// 动画,然后调用hideModal方法(加上背景div的关联处理)
$.support.transition&&this.$element.hasClass("fade")?
this.$element.one("bsTransitionEnd",$.proxy(this.hideModal,this))
.emulateTransitionEnd(300):this.hideModal();
}
//解绑并为document对象注册focusin.bs.modal事件, 具体处理是:如果不是model产生的,就触发model的facus事件
// $element获取焦点
Modal.prototype.enforceFocus = function(){
$(document).off("focusin.bs.modal").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
Modal.prototype.hideModal = function(){
var that = this;
// 调用jq的隐藏函数 hide()
this.$element.hide();
// 隐藏背景
this.backdrop(function(){
// 撤销为body加上的class modal-open (overflow:hidden)
that.$body.removeClass("modal-open");
// 还原 为了设置滚动条(使滚动条不可见) body加上的padding-right
that.resetScrollbar();
// 触发hidden.bs.modal事件
that.$element.trigger("hidden.bs.modal");
});
}
// 移除背景div modal-backdrop
Modal.prototype.removeBackdrop = function(){
this.$backdrop && this.$backdrop.remove();
this.$backdrop = null;
}
// callback为具体的model的隐藏或显示逻辑,backdrop负责背景div逻辑
Modal.prototype.backdrop = function(callback){
var that = this;
var animate = this.$element.hasClass("fade")?"fade":"";
if(this.isShown && this.options.backdrop){
//$.support.transition { end: "webkitTransitionEnd"}
var doAnimate = $.support.transition && animate;
this.$backdrop = $('<div class="modal-backdrop '+ animate +'"></div>')
.prependTo(this.$element)
.on("click.dismiss.bs.modal",$.proxy(function(e){ //绑定click.dismiss.bs.modal事件
if(e.target !== e.currentTarget) return;
// 如果传入option backdrop参数是static 则为静态背景获取焦点 否则隐藏模态框
this.options.backdrop == "static"?this.$element[0].focus().call(this.$element[0]):this.hide.call(this);
},this))
// 准备动画
if(doAnimate) this.$backdrop[0].offsetWidth
this.$backdrop.addClass("in");
if(!callback) return;
//背景div有动画就动画后回调,没有直接回调, 回调是指(显示或关闭逻辑)
doAnimate?this.$backdrop.one("bsTransitionEnd",callback).emulateTransitionEnd(150):callback();
}else if(!this.isShown && this.$backdrop){ //关闭modal框
this.$backdrop.removeClass("in");
//回调函数: 移除遮罩div后回调
var callbackRemove = function(){
that.removeBackdrop()
callback && callback()
}
// 有动画则调用动画
$.support.transition?this.$element.one("bsTransitionEnd",callbackRemove).emulateTransitionEnd(150):callbackRemove();
}else if(callback){
callback();
}
}
Modal.prototype.checkScrollbar = function(){
this.scrollbarWidth = this.measureScrollbar();
}
// 设置body内边距
Modal.prototype.setScrollbar = function(){
var bodyPad = parseInt((this.$body.css("padding-right") || 0),10);
if(this.scrollbarWidth) this.$body.css("padding-right",bodyPad+this.scrollbarWidth);
}
// 还原设置的body内边距
Modal.prototype.resetScrollbar = function(){
this.$body.css('padding-right', '');
}
// 获取滚动条宽度
Modal.prototype.measureScrollbar = function(){
// 如果有滚动条 window.innerWidth >= document.body.clientWidth
// 如果没有滚动条 window.innerWidth = document.body.clientWidth
//如果没有滚动条
if(document.body.clientWidth >= window.innerWidth) return 0;
// 如果有滚动条 创建一个有滚动条的div 获取滚动条宽度
var scrollDiv = document.createElement('div');
scrollDiv.className = "modal-scrollbar-measure";
this.$body.append(scrollDiv);
var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth;
// 移除刚才创建的div
this.$body[0].removeChild(scrollDiv);
return scrollbarWidth;
}
function Plugin(option,_relatedTarget){
// _relatedTarget目标target <a class="btn btn-default" data-toggle="modal" data-target="#delete">xx</a>
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"){ //点击按钮 toggle hide show
data[option](_relatedTarget);
// 未知option data表示new Modal
// data[option]表示 Modal.show /Modal.hide /Modal.toggle
}else if(options.show){ //初始化为显示 则显示
data.show(_relatedTarget)
}
});
}
var old = $.fn.modal;
$.fn.modal = Plugin;
$.fn.modal.Constructor = Modal;
$.fn.modal.noConflict = function(){
$.fn.modal = old;
return this;
}
$(document).on("click.bs.modal.data-api","[data-toggle='modal']",function(e){
var $this = $(this);
var href = $this.attr("href");
// 获取目标模态框id
var $target = $($this.attr("data-target") || (href&&href.replace(/.*(?=#[^\s]+$)/,'')));
/****
$this.data()获取 data-toggle="modal" data-target="#delete"
$this.data() = Object{
target : "#delete",
toggle : "modal"
}
{ remote: !/#/.test(href) && href } -> {remote: false/href}
****/
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){
//如果前面注册的事件处理器一定调用了preventDefault方法,就不会显示,后面也就不绑定隐藏事件了,所以这里也不要处理了
if(showEvent.isDefaultPrevented()) return;
$target.one("'hidden.bs.modal",function(){
$this.is(":visible") && $this.trigger("focus");
});
});
Plugin.call($target,option,this);
});
})(jQuery)
html:
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<link rel="stylesheet" href="css/modal.css">
</head>
<body>
<a class="btn btn-default" data-toggle="modal" data-target="#delete" href="#ccc">删除</a>
<div class="modal fade" id="delete" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span></button>
</div>
<div class="modal-body"></div>
<div class="modal-footer">
<button type="button" data-dismiss="modal">取消</button>
<button type="button">确定</button>
</div>
</div>
</div>
</div>
<script src="js/jquery-1.12.3.min.js"></script>
<script src="js/modal.js"></script>
<script>
$(function(){
$('#delete').modal();
// $('#delete').modal({backdrop: "static"});
// $('#delete').on("show.bs.modal",function(e){ console.log("show.bsssss") });
// $('#delete').on("shown.bs.modal",function(e){ console.log("shown.bsssss") });
// $('#delete').on('hide.bs.modal', function (e) {
// e.preventDefault();
// console.log("hide.bssss")
// })
})
</script>
</body>
</html>