其实滚动监听使用的情况还是很多的,比如导航居于右侧,当主题内容滚动某一块的时候,右侧导航对应的要高亮。
实现功能
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> ="#two">two="#three">threeul> divdata-spy="scroll" data-target="#selector" style="height:100px; overflow:hidden;overflow-y: auto;" h4 ="one" >ibeh4p>One的具体内容br/>One的具体内容/></="two" ="three" >
下面来看一下实现的具体代码,原理:当滚动容器内的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)) .refresh() .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 = () { var that = this var offsetMethod = 'offset' var offsetBase = 0 this.offsets =this.targets =this.scrollHeight = .getScrollHeight() if (!$.isWindow(this.$scrollElement[0])) { offsetMethod = 'position' offsetBase = .$scrollElement.scrollTop() } .$body .find(.selector) .map( () { var $el = $() 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( () { that.offsets.push(this[0]) that.targets.push(this[1]) }) }
它主要实现了什么呢?
1.默认用offset来获取定位值,如果滚动区域不是window则用position来获取
.$scrollElement.scrollTop()
}
2.根据导航上的hashkey来遍历获取 滚动区域内的hashkey对应的offset值:
])
})
process方法
ScrollSpy.prototype.process = var scrollTop = this.$scrollElement.scrollTop() + .options.offset var scrollHeight = .getScrollHeight() var maxScroll = this.options.offset + scrollHeight - .$scrollElement.height() var offsets = .offsets var targets = .targets var activeTarget = .activeTarget var i if (this.scrollHeight != scrollHeight) { .refresh() } if (scrollTop >= maxScroll) { return activeTarget != (i = targets[targets.length - 1]) && .activate(i) } if (activeTarget && scrollTop < offsets[0]) { this.activeTarget = null return .clear() } for (i = offsets.length; i--;) { activeTarget != targets[i] && scrollTop >= offsets[i] && (offsets[i + 1] === undefined || scrollTop < offsets[i + 1]) && .activate(targets[i]) } }
主要作用:
1.获取滚动容器已滚动距离:
this.options.offset
2.滚动容器可以滚动的最大高度:
this.$scrollElement.height()
3.设置滚动元素逻辑,给当前匹配元素添加高亮:
.activate(targets[i])
}
active方法
设置指定的导航菜单高亮
ScrollSpy.prototype.activate = (target) { this.activeTarget = target .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 = () { $(.selector) .parentsUntil(this.options.target,'.active') .removeClass('active') }
源码
+ ($) { 'use strict'; // SCROLLSPY CLASS DEFINITION ========================== .process() } ScrollSpy.VERSION = '3.3.7' ScrollSpy.DEFAULTS = { offset: 10 } ScrollSpy.prototype.getScrollHeight = this.$body[0].scrollHeight,document.documentElement.scrollHeight) } ScrollSpy.prototype.refresh = ]) }) } ScrollSpy.prototype.process = .activate(targets[i]) } } ScrollSpy.prototype.activate = ) } ScrollSpy.prototype.clear = ) } SCROLLSPY PLUGIN DEFINITION =========================== Plugin(option) { this.each( () { var $this = $() var data = $this.data('bs.scrollspy'var options = typeof option == 'object' && option if (!data) $this.data('bs.scrollspy',(data = new ScrollSpy(,options))) typeof option == 'string') data[option]() }) } var old = $.fn.scrollspy $.fn.scrollspy = Plugin $.fn.scrollspy.Constructor = ScrollSpy SCROLLSPY NO CONFLICT ===================== $.fn.scrollspy.noConflict = () { $.fn.scrollspy = old } SCROLLSPY DATA-API ================== $(window).on('load.bs.scrollspy.data-api',1)"> () { $('[data-spy="scroll"]').each(var $spy = $() Plugin.call($spy,$spy.data()) }) }) }(jQuery);