bootstrap modal.js解析

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">&times;</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>

原地址:https://my.oschina.net/haogrgr/blog/323079?p=1

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值