前面的話 滾動監聽插件是用來根據滾動條所處的位置來自動更新導航項的。滾動導航條下麵的區域並關註導航項的變化,下拉菜單中的條目也會自動高亮顯示。本文將詳細介紹Bootstrap滾動監控器 基本用法 滾動監聽插件是根據滾動的位置自動更新導航條中相應的導航項的,該插件可自動檢測到達哪個位置了,然後在需要高 ...
前面的話
滾動監聽插件是用來根據滾動條所處的位置來自動更新導航項的。滾動導航條下麵的區域並關註導航項的變化,下拉菜單中的條目也會自動高亮顯示。本文將詳細介紹Bootstrap滾動監控器
基本用法
滾動監聽插件是根據滾動的位置自動更新導航條中相應的導航項的,該插件可自動檢測到達哪個位置了,然後在需要高亮的菜單父元素上加了一個active樣式
如果導航里有下拉菜單,並且滾動區域的內容到達下拉菜單子項所對應的區域,除了子菜單高亮之外,子菜單的父元素(dropdown按鈕)也會高亮
在平時使用的過程中,滾動監聽一般有兩種用法,一種是固定一個元素的高度,進行滾動,然後對相應的菜單進行高亮顯示;另外一種是對整個頁面(body)進行滾動監聽。兩種方式的用法一樣,都需要有如下3個步驟:
1、設置滾動容器,即在所要監聽的元素上設置data-target="#selector" data-spy="scroll"屬性
2、設置菜單鏈接容器,該容器的id(或樣式)和data-target屬性所對應的選擇符要一致
3、在菜單容器內,必須有.nav樣式的元素,並且在其內容有li元素,li內包含的a元素也是可以偵測高亮的菜單鏈接,即符合.nav li > a這種選擇符的條件
4、無論何種實現方式,滾動監聽都需要被監聽的組件是 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>
JS調用
在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>
JS源碼
【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.modal代碼(如果定義的話),以便在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()) }) })