先看bootstrap.dropdown.js的結構javascript
function ScrollSpy(){} //構造函數 ScrollSpy.prototype = {} //構造器的原型 $.fn.scrollspy = function ( option ){} //jQuery原型上的自定義方法 $.fn.scrollspy.Constructor = ScrollSpy // jQuery原型上的自定義方法 $.fn.scrollspy.defaults = {} //默認參數 $(function(){}) //初始化執行
HTML結構css
<style type="text/css"> .scrollspy-example { height: 200px; overflow: auto; position: relative;} </style> <div id="navbarExample" class="navbar navbar-static"> <div class="navbar-inner"> <div class="container" style="width: auto;"> <a class="brand" href="#">項目名稱</a> <ul class="nav"> <li class="active"> <a href="#fat">紅利</a> </li> <li> <a href="#mdo">哈芬</a> </li> </ul> </div> </div> </div> <div class="scrollspy-example" data-offset="0" data-target="#navbarExample" data-spy="scroll"> <h4 id="fat">紅利</h4> <p> 中方向美方承諾提升國有企業紅利上繳比例,增長上繳利潤的中央國企和省級國企的數量,將國有資本經營預算歸入國家預算體系。還承諾,鼓勵包括國有公司在內的上市公司增長紅利支付。還承諾在信貸提供、稅收優惠和監管政策等方面對各種全部制企業一視同仁。 <br> <br> 美方認爲,提升國有企業分成比例帶來的收入可用於資助政府的社保和養老開支,從而有可能下降中國人大量儲蓄的必要性,讓他們提升消費支出,從而達到刺激中國內需的目的。 </p> <h4 id="mdo">哈芬</h4> <p> 據估,中國高鐵槽道市場約十幾億元,德國哈芬佔70%。業內人士稱,中鐵設計院的鐵道圖紙,直接指定使用哈芬,而非技術標準。哈芬在德國使用成本高昂的不鏽鋼,在中國則是碳鋼。更有業內人士證明,目前中國高鐵用的實爲國內生產,原產幾乎不足四分之一。(財新) </p> <h4 id="one">遛狗</h4> <p> 近日,拍攝於四川綿陽街頭的一張照片引發熱議,一輛在路上行駛的法院警車,車窗裏伸出一個寵物狗的腦殼,四處張望。此情景被懷疑是公務人員私用警車帶寵物狗兜風。經調查得知,這是我國新近引進的一批特殊品種警犬,爲麻痹犯罪分子,故意化妝成寵物狗的樣子。 </p> <h4 id="two">失蹤</h4> <p> 4月25日,19歲的韓耀在雲南省昆明市晉寧縣晉城鎮南門村鑫雲冷庫附近失蹤。家眷在尋找時,居然發現這一區域已前後有8名青少年失蹤,其中近一年內就有6人。有一名青年雷玉生就在此地的大街上被人拖進了一面包車,被扔進黑磚窯強迫勞動,後逃離黑磚窯重獲自由。 </p> <h4 id="three">耳光</h4> <p> 30多歲女人直接吐東西在剛掃過的地上,環衛大姐上去說了兩句,結果捱了三巴掌三腳。見到被打的環衛大姐時,她精神很差,坐在凳子上不說話,左臉的傷痕還很顯眼,工友在一旁照料她。2012年5月4日,浙江省,杭州市。 </p> <p> 尹大姐說:「小孩子都知道不能在街上亂吐。」那女人說:那不就是大家環衛應該作的事情嗎?尹大姐說:難道咱們環衛工人就低人一等嗎?」話音剛落,「啪」「啪」「啪」三個巴掌落在尹大姐臉上。 </p> </div>
咱們從初始化開始java
/* * 初始化執行 * */ $(function () { $('[data-spy="scroll"]').each(function () { var $spy = $(this) $spy.scrollspy($spy.data()) }) })
根據HTML結構,這個很簡單,獲取body對象,並執行jQuery原型上的方法scrollspy,其中$spy.data()中的值,前幾回的源碼分析,這個值已經很清楚了,它擁有{spy:'scroll',target:'#navbarExample',offset :0 },進入jQuery的原型方法bootstrap
/* * jQuery原型上的自定義方法 * */ $.fn.scrollspy = function ( option ) { return this.each(function () { var $this = $(this) , data = $this.data('scrollspy') , options = typeof option == 'object' && option; if (!data) $this.data('scrollspy', (data = new ScrollSpy(this, options)))//實例化ScrollSpy if (typeof option == 'string') data[option]()//傳入方法名,執行該方法 }) }
跟modal的原型源碼差很少,若是$this.data()中沒有scrollspy屬性則實例化構造器,另外該方法支持傳入方法名,執行擁有方法名的方法。下面是構造器api
/* * 構造器 * */ function ScrollSpy( element, options) { var process = $.proxy(this.process, this)//給原型上的process方法在該做用域下取了個別稱 , $element = $(element).is('body') ? $(window) : $(element) , href //console.log($element);//window對象 this.options = $.extend({}, $.fn.scrollspy.defaults, options) //console.log(this.options); this.$scrollElement = $element.on('scroll.scroll.data-api', process)//綁定scorll事件,執行process方法,返回window的jQuery對象 this.selector = (this.options.target || ((href = $(element).attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7 || '') + ' .nav li > a' //獲取頭標題的各個a標籤 this.$body = $('body').on('click.scroll.data-api', this.selector, process)//給頭標題的各個a標籤綁定事件,以便點擊後能夠跳到指定的內容,返回body的jQuery對象 this.refresh() this.process()//調用原型上的process方法 }
構造器完成的事情主要有3件:1.給window對象綁定scroll事件,2.給body對象中的頭標題中的a標籤綁定click事件,3.初始化執行refresh()和process()方法。我執行初始化的步驟數組
進入refresh()方法。函數
/* * position() 方法返回匹配元素相對於父元素的位置(偏移)。該方法返回的對象包含兩個整型屬性:top 和 left,以像素計。 * 獲取各個標籤對應內容的高度 * */ , refresh: function () { this.targets = this.$body .find(this.selector)//查找頭標題的各個a標籤 .map(function () { var href = $(this).attr('href') return /^#\w/.test(href) && $(href).length ? href : null })//獲得一個object對象,根據index去獲取其中類容,有點類數組 this.offsets = $.map(this.targets, function (id) { return $(id).position().top//獲取各個標籤對應內容的高度 }) }
看基於jQuery編寫的插件源碼的時候,不少時候就是幫助本身提升對jQuery方法的認知。這個refresh方法將各個a標籤的top值返回給對象的offsets屬性。其中不乏用到map方法去執行遍歷操做。進入process方法源碼分析
/* * 邏輯控制 * 初始化時,activeTarget爲空,scrollTop爲0,for循環的第二個判斷條件scrollTop >= offsets[i]中將判斷false,當i爲0時 * ,targets[0]爲#fat時,知足for循環的前三個條件,進入activate方法, * * */ , process: function () { var scrollTop = this.$scrollElement.scrollTop() + this.options.offset , offsets = this.offsets //div內容的高度集合 , targets = this.targets //div內容的id集合對象 , activeTarget = this.activeTarget , i for (i = offsets.length; i--;) { activeTarget != targets[i] && scrollTop >= offsets[i] && (!offsets[i + 1] || scrollTop <= offsets[i + 1]) && this.activate( targets[i] ) } }
javascript在書寫判斷時,有不少比較簡潔的寫法,我講有寫的好的,那就是寫的騷,對於這個方法裏的for循環中的3個&&,若是前3個判斷有一個是false的話,都沒法進入activate這個方法。註釋中,我已經講了初始化的過程,下面我描述下點擊事件中,它是如何工做的。以HTML結構爲例。學習
初始化時,'紅利'按鈕變色,當點擊'哈芬'按鈕時,會觸發以前綁定的click事件,根據經過命名錨直接跳到命名錨的連接,下面的想對應的信息發生改變,以'哈芬'爲標題的信息引入眼簾,可是這裏須要注意是,以前咱們在window上綁定了scroll事件,下面信息內容的改變,致使了滾動條的移動,觸發了window上的scroll事件,因此咱們的一次點擊,實際上觸發了兩次事件。測試
先從點擊事件開始,進過初始化以後,activeTarget的值已經被改爲#fat,所以點擊事件在通過for循環時,是到不了activate這個方法的。你們能夠斷點調試,或者將源碼中的this.$scrollElement = $element.on('scroll.scroll.data-api', process)改爲this.$scrollElement = $element以後調試。scroll事件依舊執行process事件。看起來狀況跟上次同樣,但須要注意此時的scrollTop已經發生改變,知足條件以後,進入activate方法。
/* * 邏輯控制後續執行 * 初始化時將activeTarget設成#fat,因此默認刷新時'紅利'這塊顯示被點擊。當咱們點擊'哈芬'時,經過命名錨直接跳到命名錨的連接 * 注意其餘有兩個過程:1.點擊'哈芬'按鈕,2.滾動條運動,二者都觸發了事件。最後都進入了process方法。 * * */ , activate: function (target) { var active this.activeTarget = target//設置activeTarget屬性 this.$body .find(this.selector).parent('.active') .removeClass('active')//找到頭標題中父節點擁有active類的a標籤,並刪除a標籤的父節點上的active active = this.$body .find(this.selector + '[href="' + target + '"]') .parent('li') .addClass('active')//在頭標題中,給擁有href='xx'屬性的a標籤的父節點加上active屬性,返回li標籤 if ( active.parent('.dropdown-menu') ) { active.closest('li.dropdown').addClass('active') } }
這個方法主要是收尾工做。讓被點擊按鈕變色,同時也能達到滑倒到響應內容時,對應的按鈕變色。這裏主要的比較方式是經過內容的top高度跟window的scrollTop想比較,達到某個內容的高度,則得到了該內容對應的a標籤。
這裏咱們在談一個問題,即爲何我第一次點擊'哈芬'時,是先執行的錨跳轉仍是先執行綁定方法?其實這個問題在以前的測試中,已經獲得證實先綁定方法,再執行錨跳轉,你們能夠在綁定事件中加入alert去調試。答案如咱們所預料。
內容很少,時間恰好,以上是個人一點讀碼體會,若有錯誤,請指出,你們共通學習。