zoukankan      html  css  js  c++  java
  • bootstrap源码之滚动监听组件scrollspy.js详解

    其实滚动监听使用的情况还是很多的,比如导航居于右侧,当主题内容滚动某一块的时候,右侧导航对应的要高亮。

    实现功能

    1、当滚动区域内设置的hashkey距离顶点到有效位置时,就关联设置其导航上的指定项
    2、导航必须是 .nav > li > a 结构,并且a上href或data-target要绑定hashkey
    3、菜单上必须有.nav样式
    4、滚动区域的data-target与导航父级Id(一定是父级)要一致。

    <div id="selector" class="navbar navbar-default">  
    <ul class="nav navbar-nav">  
    <li><a href="#one">one</a> </li>  
    <li><a href="#two">two</a> </li>  
    <li><a href="#three">three</a> </li>  
    </ul>
    </div>
    <div data-spy="scroll" data-target="#selector" style="height:100px; overflow:hidden;overflow-y: auto;" >  
    <h4 id="one" >ibe</h4><p>One的具体内容<br/>One的具体内容<br/>One的具体内容<br/>One的具体内容<br/>One的具体内容<br/>One的具体内容<br/></p>  
    <h4 id="two" >two</h4><p>One的具体内容<br/>One的具体内容<br/>One的具体内容<br/>One的具体内容<br/>One的具体内容<br/>One的具体内容<br/></p>  
    <h4 id="three" >three</h4><p>One的具体内容<br/>One的具体内容<br/>One的具体内容<br/>One的具体内容<br/>One的具体内容<br/>One的具体内容<br/></p>
    </div>

    下面来看一下实现的具体代码,原理:当滚动容器内的hashkey位置距离容器顶部只有 offset设置的值,就会设置导航中对应的href高亮。

    ScrollSpy构造函数

    首先新建一个构造函数,如下:

    function ScrollSpy(element, options) {
        this.$body          = $(document.body)
        this.$scrollElement = $(element).is(document.body) ? $(window) : $(element)
        this.options        = $.extend({}, ScrollSpy.DEFAULTS, options)
        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()
      }

    该构造函数主要干了啥:

    1.基本设置,主要是设置当前滚动元素是设置的body还是具体的某一块元素;其次是导航的结构要是.nav li > a的结构,也就是你的菜单中也要有.nav这个class。

    2.监听元素滚动的时候,执行process方法。

    3.同时初始化的时候也执行了refresh与process方法。

    下面讲解一下这几个方法。

    getScrolHeight方法

    获取滚动容器的内容高度(包含被隐藏部分)

    this.$scrollElement[0].scrollHeight || Math.max(this.$body[0].scrollHeight, document.documentElement.scrollHeight)

    refresh方法

    刷新并存储滚动容器内各hashkey的值

    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)
            
            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])
          })
    
      }

    它主要实现了什么呢?

    1.默认用offset来获取定位值,如果滚动区域不是window则用position来获取

    if (!$.isWindow(this.$scrollElement[0])) {
          offsetMethod = 'position'
          offsetBase   = this.$scrollElement.scrollTop()
        }

    2.根据导航上的hashkey来遍历获取 滚动区域内的hashkey对应的offset值:

    this.$body
          .find(this.selector)
          .map(function () {
            var $el   = $(this)
            var href  = $el.data('target') || $el.attr('href')
            var $href = /^#./.test(href) && $(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 () {
            that.offsets.push(this[0])
            that.targets.push(this[1])
          })

    process方法

    滚动条事件触发函数,用于计算当前需要高亮那个导航菜单

    ScrollSpy.prototype.process = function () {
        var scrollTop    = this.$scrollElement.scrollTop() + this.options.offset
        var scrollHeight = this.getScrollHeight()
        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()
        }
    
        for (i = offsets.length; i--;) {
          activeTarget != targets[i]
            && scrollTop >= offsets[i]
            && (offsets[i + 1] === undefined || scrollTop < offsets[i + 1])
            && this.activate(targets[i])
        }
      }

    主要作用:

    1.获取滚动容器已滚动距离:

    var scrollTop    = this.$scrollElement.scrollTop() + this.options.offset

    2.滚动容器可以滚动的最大高度:

    var maxScroll    = this.options.offset + scrollHeight - this.$scrollElement.height()

    3.设置滚动元素逻辑,给当前匹配元素添加高亮:

    for (i = offsets.length; i--;) {
          activeTarget != targets[i]
            && scrollTop >= offsets[i]
            && (offsets[i + 1] === undefined || scrollTop < offsets[i + 1])
            && this.activate(targets[i])
        }

    active方法

    设置指定的导航菜单高亮

    ScrollSpy.prototype.activate = function (target) {
        this.activeTarget = target
    
        this.clear()
    
        var selector = this.selector +
          '[data-target="' + target + '"],' +
          this.selector + '[href="' + target + '"]'
    
        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方法

    清除所有高亮菜单

    ScrollSpy.prototype.clear = function () {
        $(this.selector)
          .parentsUntil(this.options.target, '.active')
          .removeClass('active')
      }

     源码

    +function ($) {
      'use strict';
    
      // SCROLLSPY CLASS DEFINITION
      // ==========================
    
      function ScrollSpy(element, options) {
        this.$body          = $(document.body)
        this.$scrollElement = $(element).is(document.body) ? $(window) : $(element)
        this.options        = $.extend({}, ScrollSpy.DEFAULTS, options)
        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()
      }
    
      ScrollSpy.VERSION  = '3.3.7'
    
      ScrollSpy.DEFAULTS = {
        offset: 10
      }
    
      ScrollSpy.prototype.getScrollHeight = function () {
        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)
            
            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])
          })
    
      }
    
      ScrollSpy.prototype.process = function () {
        var scrollTop    = this.$scrollElement.scrollTop() + this.options.offset
        var scrollHeight = this.getScrollHeight()
        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()
        }
    
        for (i = offsets.length; i--;) {
          activeTarget != targets[i]
            && scrollTop >= offsets[i]
            && (offsets[i + 1] === undefined || scrollTop < offsets[i + 1])
            && this.activate(targets[i])
        }
      }
    
      ScrollSpy.prototype.activate = function (target) {
        this.activeTarget = target
    
        this.clear()
    
        var selector = this.selector +
          '[data-target="' + target + '"],' +
          this.selector + '[href="' + target + '"]'
    
        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')
      }
    
      ScrollSpy.prototype.clear = function () {
        $(this.selector)
          .parentsUntil(this.options.target, '.active')
          .removeClass('active')
      }
    
    
      // SCROLLSPY PLUGIN DEFINITION
      // ===========================
    
      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]()
        })
      }
    
      var old = $.fn.scrollspy
    
      $.fn.scrollspy             = Plugin
      $.fn.scrollspy.Constructor = ScrollSpy
    
    
      // SCROLLSPY NO CONFLICT
      // =====================
    
      $.fn.scrollspy.noConflict = function () {
        $.fn.scrollspy = old
        return this
      }
    
    
      // SCROLLSPY DATA-API
      // ==================
    
      $(window).on('load.bs.scrollspy.data-api', function () {
        $('[data-spy="scroll"]').each(function () {
          var $spy = $(this)
          Plugin.call($spy, $spy.data())
        })
      })
    
    }(jQuery);
  • 相关阅读:
    CentOS进程资源占用高原因分析命令
    Centos下修改启动项和网络配置
    CentOS查看系统信息命令和方法
    [vim]设置vim语法高亮显示和自动缩进
    [vim]vim中有中文乱码
    setState回调
    服务器安装nginx
    小程序map
    后台合成图片
    阿里云服务器添加nginx
  • 原文地址:https://www.cnblogs.com/moqiutao/p/6568073.html
Copyright © 2011-2022 走看看