先從bootstrap.modal.js的結構看起。
function($){ var Modal = function(){} //構造器 Modal.prototype = function(){} //構造器的原型 function ..(){} //自定義方法 $.fn.modal = function(){} //在jQuery對象上自定方法 $.fn.modal.defaults = {} //設置默認屬性 $.fn.modal.Constructor = Modal //重置構造器名 $(function(){}) //初始化 }(window.jQuery)
其HTML結構
<a class="btn" data-toggle="modal" href="#myModal" >點擊觸發對話框</a> <div class="modal" id="myModal"> <div class="modal-header"> <a class="close" data-dismiss="modal">×</a> <h3>對話框標題</h3> </div> <div class="modal-body"> <p>對話框內容</p> </div> <div class="modal-footer"> <a href="#" class="btn" data-dismiss="modal">關閉</a> <a href="#" class="btn btn-primary" data-dismiss="modal">保存更新</a> </div> </div>
我們開始過一遍插件源碼:從初始化開始
$(function () { /* * 在所有有data-toggle='modal'屬性的標簽上幫上click事件,一般為a標簽 * /.*(?=#[^\s]+$)/表示如果匹配成功將#之前的信息刪除 * 對於jQuery對象,比如a標簽有data-xx類型的,在其data方法中都可以顯示 * 將a標簽data-target指向的或是href的錨點指向的元素傳入modal方法里 * */ $('body').on('click.modal.data-api', '[data-toggle="modal"]', function ( e ) { var $this = $(this), href , $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7 , option = $target.data('modal') ? 'toggle' : $.extend({}, $target.data(), $this.data()); e.preventDefault() $target.modal(option) }) })
根據HTML提供的結構,$target為a標簽所指向的彈出框div的Jquery對象。另外$this.data()存在數據,為{toggle:'modal'},這個我在注釋中已經解釋。最后執行$target.modal(option),我們進入jQuery對象的這個方法。
$.fn.modal = function ( option ) { return this.each(function () { var $this = $(this) , data = $this.data('modal') , options = $.extend({}, $.fn.modal.defaults, $this.data(), typeof option == 'object' && option) if (!data) $this.data('modal', (data = new Modal(this, options)))//為modal綁定對象 if (typeof option == 'string') data[option]() else if (options.show) data.show() }) }
初始化一些參數,在彈出框div上加入了一個modal屬性,如果這個modal屬性不存在,則實例化Modal構造器。最后執行show方法,因為此時你已經點擊了a標簽,讓div顯示。代碼中有if(typeof option == 'string')data[option],表示代碼支持傳入方法名,執行該方法,在執行show方法之前,我們先進入Modal的構造器中看看。
/* * 構造器,為所有data-dismiss='modal'屬性的標簽綁定原型上的hide方法 * */ var Modal = function ( content, options ) { this.options = options this.$element = $(content) .delegate('[data-dismiss="modal"]', 'click.dismiss.modal', $.proxy(this.hide, this)) }
很簡單,讓帶有data-dismiss='modal'屬性的標簽的click事件交給彈出框div代理,注意這里的執行的方法。這里為什么要$.proxy(this.hide,this)這樣寫,第一,需要明白此時的this表示着構造器的實例,代理的事件需要使用到實例的hide方法,為什么實例擁有hide方法,很簡單this是Modal的實例,它繼承了Modal原型的方法。那proxy方法中的第二個this,則表示着這個hide方法的調用者,即上下文,可以試想一下,如果我們不修改這個,那執行這個方法時,其里面的this是不是指向了調用它的a標簽呢?(注意留意HTML結構,擁有data-dismiss屬性的三個a標簽,其實分別就是彈出框的關閉按鈕等),那原來定義在原型上的hide方法中的this的屬性將不能在正常使用。
綜上,實例化,主要就是給彈出框綁定了hide事件,為將來關閉彈出框,做准備。
好,回到之前JQuery的modal方法上,最后我們執行了show方法,進入show方法。
下面是執行的流程圖,就是各個方法的執行順序
show -> escape -> backdrop
hide ->escape -> hideModal -> backdrop
show
, show: function () { var that = this if (this.isShown) return $('body').addClass('modal-open')//給body加上modal-open類 this.isShown = true//將isShown屬性設置成true this.$element.trigger('show');//沒理解
escape.call(this)//加上鍵盤事件 backdrop.call(this, function () { var transition = $.support.transition && that.$element.hasClass('fade') //console.log(that.$element); /* * 插入彈出層 * */ !that.$element.parent().length && that.$element.appendTo(document.body) //don't move modals dom position that.$element .show()//jQuery方法顯示 if (transition) { that.$element[0].offsetWidth // force reflow } that.$element.addClass('in') transition ? that.$element.one($.support.transition.end, function () { that.$element.trigger('shown') }) : that.$element.trigger('shown') }) }
談到show和hide的時候,我們需要留意它們源碼中的一些我們認為的'廢'操作,其實它能幫助我們實現擴展。比如給body加modal-open這個類,如果你要在打開彈出層,執行其他程序,可以通過判斷這個類來實現,這是bootstrap它自身已經提供的標識。escape()方法是一個添加鍵盤事件和刪除鍵盤事件的方法,它可以讓你按enter鍵也能關閉彈出框,比較簡單。
接下來我們進入backdrop方法
/* * 生成遮罩層,並且控制遮罩層的顯示或刪除 * */ function backdrop( callback ) { var that = this , animate = this.$element.hasClass('fade') ? 'fade' : '' if (this.isShown && this.options.backdrop) { var doAnimate = $.support.transition && animate //console.log(doAnimate); this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />') .appendTo(document.body)//遮罩層,讓彈出框顯示時,背景被遮罩,此時為透明的 if (this.options.backdrop != 'static') { this.$backdrop.click($.proxy(this.hide, this)) } if (doAnimate) this.$backdrop[0].offsetWidth // force reflow this.$backdrop.addClass('in')//加入in類,顯示遮罩層 doAnimate ? this.$backdrop.one($.support.transition.end, callback) : callback() //打開時,執行callback()回調 } else if (!this.isShown && this.$backdrop) { this.$backdrop.removeClass('in')//刪除in,遮罩層透明 $.support.transition && this.$element.hasClass('fade')? this.$backdrop.one($.support.transition.end, $.proxy(removeBackdrop, this)) : removeBackdrop.call(this) //一般執行removeBackdrop()函數 } else if (callback) { callback() } }
這里為什么要判斷彈出框是否有fade類,在bootstrap.css中,.fade擁有left:-25%的屬性,當.fade .in時則left變為10%,在版本較高的瀏覽器中,有自上向下的移動的效果(低版本瀏覽器沒有效果,不過這個可以jQuery實現...),這個部分需要我們結合css樣式看,注意in的樣式,就可以了,彈出框顯示的最后一步,執行callback回調,顯示彈出框,並給彈出框加上in類,80%透明度。最后完成顯示。
之前因為我們已經在彈出框的三個a標簽上都綁定了hide事件,所以當我們點擊關閉時,執行hide方法
hide: function ( e ) { e && e.preventDefault() if (!this.isShown) return var that = this this.isShown = false $('body').removeClass('modal-open') escape.call(this)//關閉按鍵事件 this.$element .trigger('hide') .removeClass('in')//jQuery方法,將彈出框隱藏,並將div的class去in $.support.transition && this.$element.hasClass('fade') ? hideWithTransition.call(this) : hideModal.call(this) }
其實跟show方法差不多,主要是給show擦屁股,進入hideModal方法
function hideModal( that ) { this.$element .hide() //刪除效果更好 .trigger('hidden') backdrop.call(this) }
蠻蛋疼的方法,還是調用backdrop。最后刪除時我們調用的是removeBackdrop方法。
/* * 清除遮罩層 * */ function removeBackdrop() { this.$backdrop.remove() this.$backdrop = null }
將之前創建的遮罩層刪除。以上完成一遍彈出框的打開顯示。
內容不多,時間剛好,以上是我的一點讀碼體會,如有錯誤,請指出,大家共通學習。