Bootstrap滚动监听组件scrollspy.js使用方法详解


Posted in Javascript onJuly 20, 2017

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

实现功能

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 + '" rel="external nofollow" rel="external nofollow" ]'

  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 + '" rel="external nofollow" rel="external nofollow" ]'

  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);

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
javascript事件模型代码
Jul 01 Javascript
jQuery 滑动方法slideDown向下滑动元素
Jan 16 Javascript
分享纯手写漂亮的表单验证
Nov 19 Javascript
JavaScript中Function函数与Object对象的关系
Dec 17 Javascript
Bootstrap进度条组件知识详解
May 01 Javascript
JQuery点击行tr实现checkBox选中的简单实例
May 26 Javascript
Javascript 5种方法实现过滤删除前后所有空格
Jun 22 Javascript
利用jQuery的动画函数animate实现豌豆发射效果
Aug 28 Javascript
原生js代码实现图片放大境效果
Oct 30 Javascript
vue-cli3.0如何使用CDN区分开发、生产、预发布环境
Nov 22 Javascript
JavaScript实现随机五位数验证码
Sep 27 Javascript
文章或博客自动生成章节目录索引(支持三级)的实现代码
May 10 Javascript
微信小程序获取微信运动步数的实例代码
Jul 20 #Javascript
Javascript别踩白块儿(钢琴块儿)小游戏实现代码
Jul 20 #Javascript
angular动态删除ng-repaeat添加的dom节点的方法
Jul 20 #Javascript
如何使用JS在HTML中自定义字符串格式化
Jul 20 #Javascript
详解用webpack把我们的业务模块分开打包的方法
Jul 20 #Javascript
关于webpack代码拆分的解析
Jul 20 #Javascript
webpack学习笔记之代码分割和按需加载的实例详解
Jul 20 #Javascript
You might like
4月1日重磅发布!《星际争霸II》6.0.0版本更新
2020/04/09 星际争霸
摩卡咖啡
2021/03/03 咖啡文化
深入解析PHP中逗号与点号的区别
2013/08/05 PHP
PHP header()函数常用方法总结
2014/04/11 PHP
PHP中字符安全过滤函数使用小结
2015/02/25 PHP
php写入数据到CSV文件的方法
2015/03/14 PHP
Yii输入正确验证码却验证失败的解决方法
2017/06/06 PHP
PhpStorm本地断点调试的方法步骤
2018/05/21 PHP
php中上传文件的的解决方案
2018/09/25 PHP
Yii2.0建立公共方法简单示例
2019/01/29 PHP
始终在屏幕中间显示Div的代码(css+js)
2011/03/10 Javascript
js实现连个数字相加而不是拼接的方法
2014/02/23 Javascript
JQuery Tips相关(1)----关于$.Ready()
2014/08/14 Javascript
javascript面向对象之this关键词用法分析
2015/01/13 Javascript
原生JS封装ajax 传json,str,excel文件上传提交表单(推荐)
2016/06/21 Javascript
js模仿微信朋友圈计算时间显示几天/几小时/几分钟/几秒之前
2017/04/27 Javascript
JS中定位 position 的使用实例代码
2017/08/06 Javascript
微信小程序movable view移动图片和双指缩放实例代码
2017/08/08 Javascript
Web开发使用Angular实现用户密码强度判别的方法
2017/09/27 Javascript
vue实现模态框的通用写法推荐
2018/02/26 Javascript
element-ui 表格数据时间格式化的方法
2018/08/24 Javascript
Bootstrap 按钮样式与使用代码详解
2018/12/09 Javascript
vue点击自增和求和的实例代码
2019/11/06 Javascript
通过实例了解Nodejs模块系统及require机制
2020/07/16 NodeJs
微信小程序实现购物车小功能
2020/12/30 Javascript
[00:56]PWL开团时刻DAY8——追追追追追!
2020/11/09 DOTA
Python读取mp3中ID3信息的方法
2015/03/05 Python
python中ImageTk.PhotoImage()不显示图片却不报错问题解决
2018/12/06 Python
CSS Grid布局教程之什么是网格布局
2014/12/30 HTML / CSS
我为党旗添光彩演讲稿
2014/09/10 职场文书
2015元旦晚会主持词(开场白+结束语)
2014/12/14 职场文书
金秋助学感谢信
2015/01/21 职场文书
2016年社区“我们的节日·中秋节”活动总结
2016/04/05 职场文书
html5中sharedWorker实现多页面通信的示例代码
2021/05/07 Javascript
Python List remove()实例用法详解
2021/08/02 Python
SQL Server2019安装的详细步骤实战记录(亲测可用)
2022/06/10 SQL Server