Bootstrap滾動監控器

前面的話

  滾動監聽插件是用來根據滾動條所處的位置來自動更新導航項的。滾動導航條下面的區域並關注導航項的變化,下拉菜單中的條目也會自動高亮顯示。本文將詳細介紹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>

 

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.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())
    })
  })
相關文章
相關標籤/搜索