Bootstrapプラグインscrollspyソースの勉強


使い方
3つの部分に分けることができます.スクロールの要素を監視し、aラベルを含む容器、対応するアンカーポイントを含むcontent
  • スクロールをモニターする要素にdata-spy=「scroll」data-ta-ta-taget=「〓example」を追加します.これらの値は、Pluginを初期化する際に、オプトモーションオブジェクトとしての属性がSrollSpyに入る
  • aタグを含む容器には、data-targetに対応するクラスまたはidが必要です.aラベルは、data-targetまたはhrefで対応するcontentにおけるアンカーポイントを指定するid
  • コンテントにはそれぞれのアンカーポイントに対応するidが必要で、aラベルと関連
  • 
      
      
      

    @item1

    @item2

    @item3

    核心的な考え
  • ページローディングが完了したら、すべてのdata-spy=「scroll」要素をScrrollSpyの一例として実装します.スクロールイベントをバインドし、ナビゲーションバーを見つけ、対応するアンカーポイントとアンカーポイントのoffsets
  • スクロール時に、スクロール高さが最大のスクロール高さより大きいかどうかを判断し、最後のナビゲーションバーをハイライトする.スクロールの高さが最小offsetより小さいかどうかを再判定し、高明をクリアします.最後に最大スクロール高さより小さくて、最小スクロール高さより大きくて、どのセグメントoffsetにあるかを3つの&&&&&&で判断して、より高い明るさで対応するナビゲーションバー
  • 現在の明るいナビゲーションバーをクリアし、現在対応するナビゲーションバーをハイライトする
  • ナビゲーションバーがaタグの場合、hrefはidを指すので、ナビゲーションバーをクリックすると自動的に対応するConttent
  • 初期化
    オンラインの後、data-spy属性を含むラベルごとに、Pluginの初期化を完了します.
    $(window).on('load.bs.scrollspy.data-api', function () {
      $('[data-spy="scroll"]').each(function () {
        var $spy = $(this)
        Plugin.call($spy, $spy.data())
      })
    })
    Plugin入口分析
    /**
     *  [data-spy="scroll"] dom  ,   property      ScrollSpy    
     * @param {object} option          property  ,  spy target
     *                                content   , {target: '#nav-example'}
     */
    function Plugin(option) {
      return this.each(function () {
        var $this   = $(this)
        var data    = $this.data('bs.scrollspy')
        var options = typeof option == 'object' && option
    
        if (!data) $this.data('bs.scrollspy', (data = new ScrollSpy(this, options)))
        if (typeof option == 'string') data[option]()
      })
    }
    構造分析
    //     ,           、nav (selector),        
    function ScrollSply(element, options){};
    //        scrollHeigth(    ),     body     
    ScrollSpy.prototype.getScrollHeight = function () {};
    //   content             
    ScrollSpy.prototype.refresh = function () {};
    //          
    ScrollSpy.prototype.process = function () {};
    //      target    li
    ScrollSpy.prototype.activate = function (target) {};
    //          
    ScrollSpy.prototype.clear = function () {};
    具体的に分析する
  • コンストラクション
  • /**
     *       、nav  、      、   refresh() process()
     * @param {object} element   [data-spy="scroll"] dom  
     * @param {object} options   offsets   
     */
    function ScrollSpy(element, options) {
      this.$body          = $(document.body)
      //     ,     body ,      window,        
      this.$scrollElement = $(element).is(document.body) ? $(window) : $(element) 
      this.options        = $.extend({}, ScrollSpy.DEFAULTS, options)
      //    target( nav)  a    selector
      //      'target .nav li > a',      target   .nav    
      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))
      this.refresh()
      this.process()
    }
  • get ScrrollHeight方法
  • /**
     *              , body     
     * @return {number}     
     */
    ScrollSpy.prototype.getScrollHeight = function () {
      // scrollHeigth        ,  overflow        
      // this.$body[0].scrollHeight document.body.scrollHeight      
      //  DTD       ,document.documentElement.scrollHeight document.body.scrollHeight           
      //    Math.max        
      return this.$scrollElement[0].scrollHeight || Math.max(this.$body[0].scrollHeight, document.documentElement.scrollHeight)
    }
  • refresh方法
  • /**
     *    a       (  targets ),    (  offsets )
     * @return {undefined}     
     */
    ScrollSpy.prototype.refresh = function () {
      var that          = this
      var offsetMethod  = 'offset'
      var offsetBase    = 0
    
      this.offsets      = []
      this.targets      = []
      this.scrollHeight = this.getScrollHeight()
      //            body,   position     offsets 
      // jquery offset()                   
      // position()                 
      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)
    
          //    [offsets,  ]     
          // jquery map    ,return  return[ ]       。return [[ ]]            
          return ($href 
            && $href.length
            && $href.is(':visible')
            && [[$href[offsetMethod]().top + offsetBase, href]]) || null
        })
        .sort(function (a, b) { return a[0] - b[0] })
        .each(function () {
          that.offsets.push(this[0])
          that.targets.push(this[1])
        })
    }
  • process方法
  • /**
     *   this.offsets    scrollTop  ,      activate
     * @return {undefined}      
     */
    ScrollSpy.prototype.process = function () {
      var scrollTop    = this.$scrollElement.scrollTop() + this.options.offset //     offset ,      
      var scrollHeight = this.getScrollHeight() //        
      var maxScroll    = this.options.offset + scrollHeight - this.$scrollElement.height() // offset +    -              
      var offsets      = this.offsets
      var targets      = this.targets
      var activeTarget = this.activeTarget //      nav
      var i
    
      if (this.scrollHeight != scrollHeight) {
        this.refresh()
      }
      //               ,        nav
      if (scrollTop >= maxScroll) {
        return activeTarget != (i = targets[targets.length - 1]) && this.activate(i)
      }
      //       offset,         
      if (activeTarget && scrollTop < offsets[0]) {
        this.activeTarget = null
        return this.clear()
      }
      //       ,          
      for (i = offsets.length; i--;) {
        activeTarget != targets[i] //        target      
          && scrollTop >= offsets[i] //              offset
          && (offsets[i + 1] === undefined || scrollTop < offsets[i + 1]) //                  ,           
          && this.activate(targets[i]) //    nav
      }
    }
  • activate方法
  • /**
     *       dom  (     active )
     * @param  {object} target dom  
     * @return {undefined}            
     */
    ScrollSpy.prototype.activate = function (target) {
      this.activeTarget = target //             
    
      this.clear() //          
    
      var selector = this.selector +
        '[data-target="' + target + '"],' +
        this.selector + '[href="' + target + '"]'
      //     a      li  active 
      var active = $(selector)
        .parents('li')
        .addClass('active')
    
      if (active.parent('.dropdown-menu').length) {
        active = active
          .closest('li.dropdown')
          .addClass('active')
      }
      //        
      active.trigger('activate.bs.scrollspy')
    }
  • clear方法
  • /**
     *      nav      
     * @return {[type]} [description]
     */
    ScrollSpy.prototype.clear = function () {
      $(this.selector)
        .parentsUntil(this.options.target, '.active')
        .removeClass('active')
    }
    締め括りをつける
  • $el.data()元素上のdataキャッシュデータの対象
  • Math.max互換性を利用して要素の内容高さを得る
  • jqueryのoffset()は、現在のビューの相対オフセット(bodyでロールを傍受する時に使う)を得ることができ、position()は、要素がその親要素に対するオフセット(要素内でロールを傍受する時に使う)を取得することができる
  • jqueryのmap()の奇妙な表現
  • フォーサイクルを利用して3つを追加すると、スクロール高さが条件を満たしているかどうかを判断することができる