scrollspy.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. (function (root, factory) {
  2. if (typeof define === 'function' && define.amd) {
  3. // AMD. Register as an anonymous module unless amdModuleId is set
  4. define([], function () {
  5. return (root['ScrollSpy'] = factory());
  6. });
  7. } else if (typeof exports === 'object') {
  8. // Node. Does not work with strict CommonJS, but
  9. // only CommonJS-like environments that support module.exports,
  10. // like Node.
  11. module.exports = factory();
  12. } else {
  13. root['ScrollSpy'] = factory();
  14. }
  15. }(this, function () {
  16. /**
  17. * ScrollSpy
  18. *
  19. */
  20. function ScrollSpy (wrapper, opt) {
  21. var doc = document;
  22. this.wrapper = doc.querySelector(wrapper);
  23. this.nav = this.wrapper.querySelectorAll(opt.nav);
  24. this.contents = [];
  25. this.win = window;
  26. this.body = doc.body;
  27. this.winH = this.win.innerHeight;
  28. this.className = opt.className;
  29. this.callback = opt.callback;
  30. this.init();
  31. }
  32. ScrollSpy.prototype.init = function () {
  33. this.contents = this.getContents();
  34. this.attachEvent();
  35. };
  36. ScrollSpy.prototype.getContents = function () {
  37. var targetList = [];
  38. for (var i = 0, max = this.nav.length; i < max; i++) {
  39. var href = this.nav[i].href;
  40. targetList.push(document.getElementById(href.split('#')[1]));
  41. }
  42. return targetList;
  43. };
  44. ScrollSpy.prototype.attachEvent = function () {
  45. this.win.addEventListener('load', (function () {
  46. this.spy(this.callback);
  47. }).bind(this));
  48. var scrollingTimer;
  49. this.win.addEventListener('scroll', (function () {
  50. if (scrollingTimer) {
  51. clearTimeout(scrollingTimer);
  52. }
  53. var _this = this;
  54. scrollingTimer = setTimeout(function () {
  55. _this.spy(_this.callback);
  56. }, 10);
  57. }).bind(this));
  58. var resizingTimer;
  59. this.win.addEventListener('resize', (function () {
  60. if (resizingTimer) {
  61. clearTimeout(resizingTimer);
  62. }
  63. var _this = this;
  64. resizingTimer = setTimeout(function () {
  65. _this.spy(_this.callback);
  66. }, 10);
  67. }).bind(this));
  68. };
  69. ScrollSpy.prototype.spy = function (cb) {
  70. var elems = this.getElemsViewState();
  71. this.markNav(elems);
  72. if (typeof cb === 'function') {
  73. cb(elems);
  74. }
  75. };
  76. ScrollSpy.prototype.getElemsViewState = function () {
  77. var elemsInView = [],
  78. elemsOutView = [],
  79. viewStatusList = [];
  80. for (var i = 0, max = this.contents.length; i < max; i++) {
  81. var currentContent = this.contents[i],
  82. isInView = this.isInView(currentContent);
  83. if (isInView) {
  84. elemsInView.push(currentContent);
  85. } else {
  86. elemsOutView.push(currentContent);
  87. }
  88. viewStatusList.push(isInView);
  89. }
  90. return {
  91. inView: elemsInView,
  92. outView: elemsOutView,
  93. viewStatusList: viewStatusList
  94. };
  95. };
  96. ScrollSpy.prototype.isInView = function (el) {
  97. var winH = this.winH,
  98. scrollTop = this.body.scrollTop,
  99. scrollBottom = scrollTop + winH,
  100. elTop = el.offsetTop,
  101. elBottom = elTop + el.offsetHeight;
  102. return (elTop < scrollBottom) && (elBottom > scrollTop);
  103. };
  104. ScrollSpy.prototype.markNav = function (elems) {
  105. var navItems = this.nav,
  106. isAlreadyMarked = false;
  107. for (var i = 0, max = navItems.length; i < max; i++) {
  108. if (elems.viewStatusList[i] && !isAlreadyMarked) {
  109. isAlreadyMarked = true;
  110. navItems[i].classList.add(this.className);
  111. } else {
  112. navItems[i].classList.remove(this.className);
  113. }
  114. }
  115. };
  116. return ScrollSpy;
  117. }));