滾動監聽插件是用來根據滾動條所處的位置來自動更新導航項的。滾動導航條下面的區域並關注導航項的變化,下拉菜單中的條目也會自動高亮顯示。本文將詳細介紹Bootstrap滾動監控器javascript
滾動監聽插件是根據滾動的位置自動更新導航條中相應的導航項的,該插件可自動檢測到達哪一個位置了,而後在須要高亮的菜單父元素上加了一個active樣式css
若是導航裏有下拉菜單,而且滾動區域的內容到達下拉菜單子項所對應的區域,除了子菜單高亮以外,子菜單的父元素(dropdown按鈕)也會高亮html
在平時使用的過程當中,滾動監聽通常有兩種用法,一種是固定一個元素的高度,進行滾動,而後對相應的菜單進行高亮顯示;另一種是對整個頁面(body)進行滾動監聽。兩種方式的用法同樣,都須要有以下3個步驟:java
一、設置滾動容器,即在所要監聽的元素上設置data-target="#selector" data-spy="scroll"屬性bootstrap
二、設置菜單連接容器,該容器的id(或樣式)和data-target屬性所對應的選擇符要一致api
三、在菜單容器內,必須有.nav樣式的元素,而且在其內容有li元素,li內包含的a元素也是能夠偵測高亮的菜單連接,即符合.nav li > a這種選擇符的條件數組
四、不管何種實現方式,滾動監聽都須要被監聽的組件是 position: relative;
即相對定位方式瀏覽器
【固定元素高度】框架
<div id="myNavbar" class="navbar navbar-default navbar-fixed-top" role="navigation" style="position:relative"> <ul class="nav navbar-nav"> <li><a href="#html" tabindex="-1">HTML</a></li> <li><a href="#css" tabindex="-1">CSS</a></li> <li><a href="#javascript" tabindex="-1">javascript</a></li> </ul> </div> <div data-spy="scroll" data-target="#myNavbar" style="margin-top:150px;height:250px;overflow:auto;position:relative"> <h4 id="html">Html</h4> <p>Html內容</p> <br><p>...</p><br><p>...</p><br><p>...</p> <h4 id="css">CSS</h4> <p>CSS內容</p> <br><p>...</p><br><p>...</p><br><p>...</p> <h4 id="javascript">javascript</h4> <p>javascript內容</p> <br><p>...</p><br><p>...</p><br><p>...</p><br><p>...</p><br><p>...</p><br><p>...</p> </div>
【body元素】函數
<body data-spy="scroll" data-target="#myNavbar" style="height:300px;position:relative"> <div id="myNavbar" class="navbar navbar-default navbar-fixed-top" role="navigation"> <ul class="nav navbar-nav"> <li><a href="#html" tabindex="-1">HTML</a></li> <li><a href="#css" tabindex="-1">CSS</a></li> <li><a href="#javascript" tabindex="-1">javascript</a></li> </ul> </div> <h4 id="html" style="margin-top:150px">Html</h4> <p>Html內容</p> <br><p>...</p><br><p>...</p><br><p>...</p> <h4 id="css">CSS</h4> <p>CSS內容</p> <br><p>...</p><br><p>...</p><br><p>...</p> <h4 id="javascript">javascript</h4> <p>javascript內容</p> <br><p>...</p><br><p>...</p><br><p>...</p><br><p>...</p><br><p>...</p><br><p>...</p> </body>
在Bootstrap框架中,使用JavaScript方法觸發滾動監控器相對來講較爲簡單,只須要指定兩個容器的名稱便可
<div id="myNavbar" class="navbar navbar-default navbar-fixed-top" role="navigation"> <ul class="nav navbar-nav"> <li><a href="#html" tabindex="-1">HTML</a></li> <li><a href="#css" tabindex="-1">CSS</a></li> <li><a href="#javascript" tabindex="-1">javascript</a></li> </ul> </div> <div id="scrollspy" style="margin-top:150px;height:250px;overflow:auto;position:relative"> <h4 id="html">Html</h4> <p>Html內容</p> <br><p>...</p><br><p>...</p><br><p>...</p> <h4 id="css">CSS</h4> <p>CSS內容</p> <br><p>...</p><br><p>...</p><br><p>...</p> <h4 id="javascript">javascript</h4> <p>javascript內容</p> <br><p>...</p><br><p>...</p><br><p>...</p><br><p>...</p><br><p>...</p><br><p>...</p> </div> <script> $('#scrollspy').scrollspy({ target: '#myNavbar' }) </script>
當使用滾動監聽插件的同時在 DOM 中添加或刪除元素後,須要像下面這樣調用此刷新( refresh) 方法
$('[data-spy="scroll"]').each(function () { var $spy = $(this).scrollspy('refresh') })
要注意的是,這種refresh方法只對聲明式用法有效。若是使用的是JS觸發,而且須要刷新DOM,則須要從新應用該插件;或者從data-scrollspy屬性上獲取該實例,而後再調用refresh方法
【參數】
能夠經過 data 屬性或 JavaScript 傳遞參數。對於 data 屬性,其名稱是將參數名附着到 data-
後面組成,例如 data-offset=""
滾動監控提供了一個offset參數,此參數默認值爲10。默認狀況下,滾動內容距離滾動容器10px之內的話,就高亮顯示所對應的菜單項
【事件】
滾動監控也支持事件的訂閱和觸發功能,目前只支持一個activate事件
activate.bs.scrollspy 每當一個新條目被激活後都將由滾動監聽插件觸發此事件。
<div id="myNavbar" class="navbar navbar-default navbar-fixed-top" role="navigation"> <ul class="nav navbar-nav"> <li><a href="#html" tabindex="-1">HTML</a></li> <li><a href="#css" tabindex="-1">CSS</a></li> <li><a href="#javascript" tabindex="-1">javascript</a></li> </ul> </div> <div id="scrollspy" data-spy="scroll" data-target="#myNavbar" data-offset="0" style="margin-top:150px;height:250px;overflow:auto;position;relative"> <h4 id="html">Html</h4> <p>Html內容</p> <br><p>...</p><br><p>...</p><br><p>...</p> <h4 id="css">CSS</h4> <p>CSS內容</p> <br><p>...</p><br><p>...</p><br><p>...</p> <h4 id="javascript">javascript</h4> <p>javascript內容</p> <br><p>...</p><br><p>...</p><br><p>...</p><br><p>...</p><br><p>...</p><br><p>...</p> </div> <script> $(function(){ $("#myNavbar").on('activate.bs.scrollspy',function(e){ $(e.target).siblings().css('outline','none') .end().css('outline','1px solid black'); }) }) </script>
【1】IIFE
使用當即調用函數,防止插件內代碼外泄,從而造成一個閉環,而且只能從jQuery的fn裏進行擴展
+function ($) { //使用es5嚴格模式 'use strict'; // }(window.jQuery);
【2】初始設置
function ScrollSpy(element, options) { this.$body = $(document.body) //判斷滾動容器是不是body,若是是則使用window,若是不是則使用該元素自己 this.$scrollElement = $(element).is(document.body) ? $(window) : $(element) //將默認值和傳進來的options參數合併,後者優先級高 this.options = $.extend({}, ScrollSpy.DEFAULTS, options) //若是option裏設置了target,即data-target有值,則優先使用 //若是沒有,則查找經過.nav樣式的子元素,即.nav樣式內的li子元素內的a連接,做爲菜單容器 this.selector = (this.options.target || '') + ' .nav li > a' this.offsets = [] this.targets = [] //高亮顯示的菜單 this.activeTarget = null this.scrollHeight = 0 //給滾動容器綁定滾動事件 this.$scrollElement.on('scroll.bs.scrollspy', $.proxy(this.process, this)) //計算當前頁面內全部滾動容器內的id集合和每一個id元素距離瀏覽器頂部的像素距離 this.refresh() //開始正式處理 this.process() } //版本是3.3.7 ScrollSpy.VERSION = '3.3.7' //默認值爲offset:10 ScrollSpy.DEFAULTS = { offset: 10 }
【3】插件核心代碼
//獲取滾動容器的滾動高度 ScrollSpy.prototype.getScrollHeight = function () { //獲取特定滾動容器的滾動高度,若是沒有則獲取body元素的滾動高度 return this.$scrollElement[0].scrollHeight || Math.max(this.$body[0].scrollHeight, document.documentElement.scrollHeight) } ScrollSpy.prototype.refresh = function () { var that = this var offsetMethod = 'offset' var offsetBase = 0 this.offsets = [] this.targets = [] this.scrollHeight = this.getScrollHeight() if (!$.isWindow(this.$scrollElement[0])) { offsetMethod = 'position' offsetBase = this.$scrollElement.scrollTop() } this.$body .find(this.selector) .map(function () { var $el = $(this) var href = $el.data('target') || $el.attr('href') var $href = /^#./.test(href) && $(href) //返回一個二維數組,每一個滾動容器內的id對象到頁面頂部的距離以及高亮菜單容器裏所對應的href值 return ($href && $href.length && $href.is(':visible') && [[$href[offsetMethod]().top + offsetBase, href]]) || null }) .sort(function (a, b) { return a[0] - b[0] }) .each(function () { //收集全部的偏移值,也就是距離top的距離 that.offsets.push(this[0]) //收集菜單容器裏的全部href值,也就是滾動容器裏的id值 that.targets.push(this[1]) }) } ScrollSpy.prototype.process = function () { //獲取滾動容器的scrollTop,再加上設置的offset值 var scrollTop = this.$scrollElement.scrollTop() + this.options.offset //獲取滾動高度 var scrollHeight = this.getScrollHeight() //最大滾動=總scrollheight + 設置的offset值 - 設置高度height var maxScroll = this.options.offset + scrollHeight - this.$scrollElement.height() var offsets = this.offsets var targets = this.targets var activeTarget = this.activeTarget var i if (this.scrollHeight != scrollHeight) { this.refresh() } //若是超過了最大滾動,說明已經滾動到底了 if (scrollTop >= maxScroll) { //若是最後一個元素尚未高亮,則設置最後一個元素高亮 return activeTarget != (i = targets[targets.length - 1]) && this.activate(i) } if (activeTarget && scrollTop < offsets[0]) { this.activeTarget = null return this.clear() } //倒序遍歷全部元素的offset for (i = offsets.length; i--;) { //若是i元素不等於當前高亮元素 activeTarget != targets[i] //滾動高度 大於 i元素的offsets && scrollTop >= offsets[i] //i+1元素不存在,或者i+1元素大於滾動高度 && (offsets[i + 1] === undefined || scrollTop < offsets[i + 1]) //則設置i爲高亮元素 && this.activate(targets[i]) } } //設置高亮菜單元素 ScrollSpy.prototype.activate = function (target) { //賦值實例屬性 this.activeTarget = target this.clear() //查找菜單中符合[data-target+"#' + 所高亮元素的id + '"]屬性的元素 //或者href值是#' + 所高亮元素的id + '的話,也能夠 var selector = this.selector + '[data-target="' + target + '"],' + this.selector + '[href="' + target + '"]' //查找父元素li,而後添加active高亮樣式 var active = $(selector) .parents('li') .addClass('active') //若是li元素的父元素有dropdown-menu樣式,則表示是一個dropdown下拉菜單 if (active.parent('.dropdown-menu').length) { active = active .closest('li.dropdown') //則須要給dropdown的li元素也加上active高亮樣式 .addClass('active') } //觸發自定義高亮事件 active.trigger('activate.bs.scrollspy') } //刪除其餘高亮元素的active樣式 ScrollSpy.prototype.clear = function () { $(this.selector) .parentsUntil(this.options.target, '.active') .removeClass('active') }
【4】jQuery插件定義
function Plugin(option) { //根據選擇器,遍歷全部符合規則的元素 return this.each(function () { var $this = $(this) //獲取自定義屬性bs.scrollspy的值 var data = $this.data('bs.scrollspy') //若是option參數是對象,則做爲ScrollSpy的參數傳入 var options = typeof option == 'object' && option //若是值不存在,則將ScrollSpy實例設置爲bs.scrollSpy值 if (!data) $this.data('bs.scrollspy', (data = new ScrollSpy(this, options))) //若是option傳遞了string,則表示要執行某個方法 if (typeof option == 'string') data[option]() }) } var old = $.fn.scrollspy //保留其餘庫的$.fn.scrollspy代碼(若是定義的話),以便在noConflict以後能夠繼續使用該老代碼 $.fn.scrollspy = Plugin //重設插件構造器,能夠經過該屬性獲取插件的真實類函數 $.fn.scrollspy.Constructor = ScrollSpy
【5】防衝突處理
$.fn.scrollspy.noConflict = function () { //恢復之前的舊代碼 $.fn.scrollspy = old //將$.fn.scrollspy.noConflict()設置爲Bootstrap的Scrollspy插件 return this }
【6】綁定觸發事件
$(window).on('load.bs.scrollspy.data-api', function () { //遍歷全部符合條件的滾動容器 $('[data-spy="scroll"]').each(function () { var $spy = $(this) //執行scrollspy插件,並傳入滾動容器上設置的自定義參數(data-開頭) Plugin.call($spy, $spy.data()) }) })