(function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define([], function () { return (factory()); }); } else if (typeof exports === 'object') { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(); } else { factory(); } }(this, function () { function ScrollSpy (wrapper, opt) { var doc = document; this.wrapper = wrapper || doc.querySelector(opt.wrapper); this.nav = this.wrapper.querySelectorAll(opt.nav); this.contents = []; this.win = window; this.body = doc.body; this.winH = this.win.innerHeight; this.className = opt.className; this.callback = opt.callback; this.init(); } ScrollSpy.prototype.init = function () { this.contents = this.getContents(); this.attachEvent(); }; ScrollSpy.prototype.getContents = function () { var targetList = []; for (var i = 0, max = this.nav.length; i < max; i++) { var href = this.nav[i].href; targetList.push(document.getElementById(href.split('#')[1])); } return targetList; }; ScrollSpy.prototype.attachEvent = function () { this.win.addEventListener('load', (function () { this.spy(this.callback); }).bind(this)); var scrollingTimer; this.win.addEventListener('scroll', (function () { if (scrollingTimer) { clearTimeout(scrollingTimer); } var _this = this; scrollingTimer = setTimeout(function () { _this.spy(_this.callback); }, 10); }).bind(this)); var resizingTimer; this.win.addEventListener('resize', (function () { if (resizingTimer) { clearTimeout(resizingTimer); } var _this = this; resizingTimer = setTimeout(function () { _this.spy(_this.callback); }, 10); }).bind(this)); }; ScrollSpy.prototype.spy = function (cb) { var elems = this.getElemsViewState(); this.markNav(elems); if (typeof cb === 'function') { cb(elems); } }; ScrollSpy.prototype.getElemsViewState = function () { var elemsInView = [], elemsOutView = [], viewStatusList = []; for (var i = 0, max = this.contents.length; i < max; i++) { var currentContent = this.contents[i], isInView = this.isInView(currentContent); if (isInView) { elemsInView.push(currentContent); } else { elemsOutView.push(currentContent); } viewStatusList.push(isInView); } return { inView: elemsInView, outView: elemsOutView, viewStatusList: viewStatusList }; }; ScrollSpy.prototype.isInView = function (el) { var winH = this.winH, scrollTop = this.body.scrollTop, scrollBottom = scrollTop + winH, elTop = el.offsetTop, elBottom = elTop + el.offsetHeight; return (elTop < scrollBottom) && (elBottom > scrollTop); }; ScrollSpy.prototype.markNav = function (elems) { var navItems = this.nav, isAlreadyMarked = false; for (var i = 0, max = navItems.length; i < max; i++) { if (elems.viewStatusList[i] && !isAlreadyMarked) { isAlreadyMarked = true; navItems[i].classList.add(this.className); } else { navItems[i].classList.remove(this.className); } } }; (function ($) { $.fn.scrollSpy = function (options) { return this.each(function () { $(this).data('scrollspy', new ScrollSpy(this, options)); }); }; })(jQuery); }));