先看bootstrap.collapse.js的結構
var Collapse = function ( element, options ){} // 構造器 Collapse.prototype = {} //構造器的原型 $.fn.collapse = function ( option ){} //jQuery原型上自定義的方法 $.fn.collapse.defaults = {} // 默認參數 $.fn.collapse.Constructor = Collapse // 重寫jQuery原型自定義方法的構造器名 $(function (){}) // 初始化
HTML結構
<div class="accordion" id="accordion2"> <div class="accordion-group"> <div class="accordion-heading"> <a class="accordion-toggle" data-toggle="collapse" data-parent="#accordion2" href="#collapseOne"> 國土問題 </a> </div> <div id="collapseOne" class="accordion-body collapse" style="height: 0px;"> <div class="accordion-inner"> 前一段時間一個段子說,某國的網民在因國土問題與中國網民爭吵時說,我要打到北京,中國的網民非常淡然地回應,就你那經濟水平,交得起過路費嗎?這兩天新的段子說,李白要是活在今天的話,估計一大半以上他的詩根本寫不出來,因為名山大川的門票他根本買不起。 </div> </div> </div> <div class="accordion-group"> <div class="accordion-heading"> <a class="accordion-toggle" data-toggle="collapse" data-parent="#accordion2" href="#collapseTwo"> 門票問題 </a> </div> <div id="collapseTwo" class="accordion-body collapse" style="height: 0px;"> <div class="accordion-inner"> 目前,中國半數5A級景區門票達到100元,黃山門票10年來由80元漲至230元。山東曲阜稱,與同類景區相比收費較低,僅收150元,不漲票價就丟身價。曲阜的孔廟、孔府和孔林,年收入1.5億元左右,全部上繳了地方財政,但景區維護成本從未公開。 </div> </div> </div> <div class="accordion-group"> <div class="accordion-heading"> <a class="accordion-toggle" data-toggle="collapse" data-parent="#accordion2" href="#collapseThree"> 超生罰款 </a> </div> <div id="collapseThree" class="accordion-body in" style="height: auto;"> <div class="accordion-inner"> 學者楊支柱因生二胎被取消公職,並罰款24萬余元。他稱,計生罰款以前直接叫超生罰款,入世后改成"社會撫養費"。根據9省市超生罰款的平均數,全國31個省市每年征收的超生罰款可高達279億元。其中大城市將該收入上繳財政,而地方則分配混亂,部分罰款去向成謎。 </div> </div> </div> </div>
我們先從初始化開始
/* * 初始化 * */ $(function () { $('body').on('click.collapse.data-api', '[data-toggle=collapse]', function ( e ) { var $this = $(this), href , target = $this.attr('data-target')//標題沒有data-target屬性,阻止冒泡 || e.preventDefault() || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7 //根據點擊的標題,獲取相對應內容的id , option = $(target).data('collapse') ? 'toggle' : $this.data()//初次執行時$(target)沒有collapse屬性 $(target).collapse(option) }) })
body監聽了所有擁有data-toggle='collapse'屬性的標簽,為它們綁定了click事件,在HTML中由於標簽上都沒有data-target屬性,所以該事件被阻止冒泡,最后獲得點擊標題框所對應的內容div的id。
當我們刷新頁面,第一次點擊時,由於$(target).data('collapse')為空,所以option為$this.data()的內容,主要有{parent:'#accordion2',toggle:'collapse'},下面執行jQuery對象上的collapse方法
/* * jQuery原型上自定義的方法 * */ $.fn.collapse = function ( option ) { return this.each(function () { var $this = $(this) , data = $this.data('collapse') , options = typeof option == 'object' && option if (!data) $this.data('collapse', (data = new Collapse(this, options)))//實例化構造器 if (typeof option == 'string') data[option]()//支持傳入方法名,執行該方法 }) }
此時調用collapse方法的是你點擊標題框所對應的內容div,這里我們需要初始化構造器。因為option此時為對象,所以最后一句不執行。
/* * 構造器 * */ var Collapse = function ( element, options ) { this.$element = $(element) this.options = $.extend({}, $.fn.collapse.defaults, options) if (this.options["parent"]) { this.$parent = $(this.options["parent"])//獲取整個控件的jQuery對象 } this.options.toggle && this.toggle()//實例化執行原型上的toggle方法 }
這里this.$parent指的是整個控件的div,id為'#accordion2',執行原型上的toggle方法
toggle: function () { this[this.$element.hasClass('in') ? 'hide' : 'show']()//實例化初次執行show方法 }
這里,查看內容div上是否有in類,其實bootstrap.collapse插件,在顯示的內容上,會有in類,不顯示的內容上會collapse類,不過,之前我們先了解。這個插件刷新時,第三塊內容顯示,ok,我們點擊第一塊試一試。,那么第一塊對應的內容div就沒有in類,所以我們執行show方法,在進入方法之前,我們可以想想下面插件需要完成的步驟:1。將之前顯示的隱藏了 2.顯示被點擊的
/* * 顯示方法 * */ , show: function () { var dimension = this.dimension() , scroll = $.camelCase(['scroll', dimension].join('-')) //'scrollHeight' , actives = this.$parent && this.$parent.find('.in')//刷新時,顯示內容的div,默認設置的 , hasData if (actives && actives.length) { //console.log(actives.data('collapse')); hasData = actives.data('collapse') actives.collapse('hide')//將開始顯示的內容隱藏了,調用hide方法。 hasData || actives.data('collapse', null) } this.$element[dimension](0)//將高度清0,完成隱藏效果 this.transition('addClass', 'show', 'shown') this.$element[dimension](this.$element[0][scroll])//將當前點擊的標簽相關聯的內容的高度恢復,結果顯示效果 }
查看show方法之前,我們先看dimension方法
dimension: function () { var hasWidth = this.$element.hasClass('width') return hasWidth ? 'width' : 'height' }
主要返回字符串,這個插件主要是上下顯示隱藏,也可以左右隱藏。這里我們返回height
scroll = $.camelCase(['scroll', dimension].join('-')) //'scrollHeight'
目的將scroll與height兩個字符串合並成scrollHeight。這種方式,我們可以了解一下。
actives = this.$parent && this.$parent.find('.in')//刷新時,顯示內容的div,默認設置的
找到頁面上目前顯示的內容對象,經過if判斷之后,這個內容對象調用了jQuery原型上的collapse方法。參數為hide,我們再次進入jQuery原型方法
/* * jQuery原型上自定義的方法 * */ $.fn.collapse = function ( option ) { return this.each(function () { var $this = $(this) , data = $this.data('collapse') , options = typeof option == 'object' && option if (!data) $this.data('collapse', (data = new Collapse(this, options)))//實例化構造器 if (typeof option == 'string') data[option]()//支持傳入方法名,執行該方法 }) }
這里的$this.data('collapse')是沒有,這個跟之前的要區分開來,之前的是你點擊的想讓它顯示的內容,而現在這個是頁面上原來就顯示的內容。所以再一次的實例化。options內容為false。別忘了,執行完初始化之后再執行data[option],先看構造器
/* * 構造器 * */ var Collapse = function ( element, options ) { this.$element = $(element) this.options = $.extend({}, $.fn.collapse.defaults, options) if (this.options["parent"]) { this.$parent = $(this.options["parent"])//此時已經沒有該對象了 } this.options.toggle && this.toggle()//實例化執行原型上的toggle方法 }
記住此時實例化的對象是當前顯示的div內容。假如我們點擊的是第一個標題對應的div,默認顯示是第三個div,那這時實例化的對象就是第三個div。下面的代碼分析,我們就這套看。
toggle: function () { this[this.$element.hasClass('in') ? 'hide' : 'show']()//實例化初次執行show方法 }
此時的this.$element上已經擁有了in類,畢竟它是默認顯示的嘛,執行原型上的hide方法。
/* * 隱藏方法 * */ , hide: function () { var dimension = this.dimension() this.reset(this.$element[dimension]()) this.transition('removeClass', 'hide', 'hidden') this.$element[dimension](0)//將高度清0 }
dimension這個就不重復說了,進入reset方法
reset: function ( size ) { var dimension = this.dimension() this.$element.removeClass('collapse')[dimension](size || 'auto')[0].offsetWidth//這句蛋疼 this.$element[size ? 'addClass' : 'removeClass']('collapse')//給要隱藏的內容div加上collapse樣式 return this }
想一想之前我們點擊第一個div,目的是讓它顯示,隱藏其他div。這邊調用reset方法的是div3,根據它的高度,增加collapse,前面我們也說了,bootstrap.collapse插件在隱藏的div上都加有collapse類,顯示的div上都有in類,因為reset方法是根據高度來判斷是否要添加class,所以在代碼后面也肯定會將高度清0的,為的是依靠判斷再次刪去class,但是感覺這個方式太繁瑣,通過高度判斷,刪減css讓div隱藏,在將div的高度清0,再通過高度判斷,向div上刪減css樣式,使其顯示,最后在恢復div的高度。如此反復。
不多廢話,進入transition方法
transition: function ( method, startEvent, completeEvent ) { //'removeClass','hide','hidden' var that = this , complete = function () { if (startEvent == 'show') that.reset() that.$element.trigger(completeEvent) } this.$element .trigger(startEvent) [method]('in')//隱藏內容,並去掉in類 $.support.transition && this.$element.hasClass('collapse') ? this.$element.one($.support.transition.end, complete) : complete() }
果不其然,就是如此實現的。
此時hide方法執行完畢,下面執行jQuery原型上的最后一句代碼
if (typeof option == 'string') data[option]()//支持傳入方法名,執行該方法
如果你們還記得,這個data,依舊是與div3有關的實例化對象。再次執行原型上的hide方法,這是這個插件我認為蛋疼的一個地方,插件的API沒有很好的跟jQuery原型綁定上,僅僅依靠調用jQuery原型方法,實例化構造器去調用原型方法。導致jQuery隊形不能直接使用原型上的方法,作者這樣做的原因也有可能是防止自己在原型上定義的方法名和jQuery方法重名。遭到覆蓋。不過換個名字,可能就解決了么。。。
再執行一遍hide,就不演示了,ok整個actives.collapse('hide')執行完畢,繼續看show方法。
hasData || actives.data('collapse', null)//將data的collapse再次重新清空
將所有與actives有關的記憶全部刪除。
this.$element[dimension](0)//將高度清0,
this.transition('addClass', 'show', 'shown') this.$element[dimension](this.$element[0][scroll])//將當前點擊的標簽相關聯的內容的高度恢復,結果顯示效果
上面的代碼,先清空div1的高度,在通過transition方法加入in類,最后恢復div1的高度,蛋疼。。
上述主要描述了div3顯示,點擊div1的情景,如果我們要點擊div3,讓它自己隱藏,讓我們來看看代碼過程。
我們點擊div3,進入jQuery的collapse方法
/* * jQuery原型上自定義的方法 * */ $.fn.collapse = function ( option ) { return this.each(function () { var $this = $(this) , data = $this.data('collapse') , options = typeof option == 'object' && option if (!data) $this.data('collapse', (data = new Collapse(this, options)))//實例化構造器 if (typeof option == 'string') data[option]()//支持傳入方法名,執行該方法 }) }
進入構造器
/* * 構造器 * */ var Collapse = function ( element, options ) { this.$element = $(element) this.options = $.extend({}, $.fn.collapse.defaults, options) if (this.options["parent"]) { this.$parent = $(this.options["parent"])//獲取整個控件的jQuery對象 } this.options.toggle && this.toggle()//實例化執行原型上的toggle方法 }
好吧,進入toggle方法
toggle: function () { this[this.$element.hasClass('in') ? 'hide' : 'show']()//實例化初次執行show方法 }
此時對象本身擁有in類,所以執行hide方法,ok,搞定。
其實這個插件還有幾種情況,這里就不一一演示了。
沒有很好的綁定jQuery原型,導致調用方法,實例化兩次構造器,使得我們走代碼的流程比較多,有點耐心,應該沒問題。
內容不多,時間剛好,以上是我的一點讀碼體會,如有錯誤,請指出,大家共通學習。