scrollspy.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. /*!
  2. * Bootstrap scrollspy.js v4.5.2 (https://getbootstrap.com/)
  3. * Copyright 2011-2020 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
  4. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
  5. */
  6. (function (global, factory) {
  7. typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('jquery'), require('./util.js')) :
  8. typeof define === 'function' && define.amd ? define(['jquery', './util.js'], factory) :
  9. (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.ScrollSpy = factory(global.jQuery, global.Util));
  10. }(this, (function ($, Util) { 'use strict';
  11. $ = $ && Object.prototype.hasOwnProperty.call($, 'default') ? $['default'] : $;
  12. Util = Util && Object.prototype.hasOwnProperty.call(Util, 'default') ? Util['default'] : Util;
  13. function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
  14. function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
  15. function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
  16. /**
  17. * ------------------------------------------------------------------------
  18. * Constants
  19. * ------------------------------------------------------------------------
  20. */
  21. var NAME = 'scrollspy';
  22. var VERSION = '4.5.2';
  23. var DATA_KEY = 'bs.scrollspy';
  24. var EVENT_KEY = "." + DATA_KEY;
  25. var DATA_API_KEY = '.data-api';
  26. var JQUERY_NO_CONFLICT = $.fn[NAME];
  27. var Default = {
  28. offset: 10,
  29. method: 'auto',
  30. target: ''
  31. };
  32. var DefaultType = {
  33. offset: 'number',
  34. method: 'string',
  35. target: '(string|element)'
  36. };
  37. var EVENT_ACTIVATE = "activate" + EVENT_KEY;
  38. var EVENT_SCROLL = "scroll" + EVENT_KEY;
  39. var EVENT_LOAD_DATA_API = "load" + EVENT_KEY + DATA_API_KEY;
  40. var CLASS_NAME_DROPDOWN_ITEM = 'dropdown-item';
  41. var CLASS_NAME_ACTIVE = 'active';
  42. var SELECTOR_DATA_SPY = '[data-spy="scroll"]';
  43. var SELECTOR_NAV_LIST_GROUP = '.nav, .list-group';
  44. var SELECTOR_NAV_LINKS = '.nav-link';
  45. var SELECTOR_NAV_ITEMS = '.nav-item';
  46. var SELECTOR_LIST_ITEMS = '.list-group-item';
  47. var SELECTOR_DROPDOWN = '.dropdown';
  48. var SELECTOR_DROPDOWN_ITEMS = '.dropdown-item';
  49. var SELECTOR_DROPDOWN_TOGGLE = '.dropdown-toggle';
  50. var METHOD_OFFSET = 'offset';
  51. var METHOD_POSITION = 'position';
  52. /**
  53. * ------------------------------------------------------------------------
  54. * Class Definition
  55. * ------------------------------------------------------------------------
  56. */
  57. var ScrollSpy = /*#__PURE__*/function () {
  58. function ScrollSpy(element, config) {
  59. var _this = this;
  60. this._element = element;
  61. this._scrollElement = element.tagName === 'BODY' ? window : element;
  62. this._config = this._getConfig(config);
  63. this._selector = this._config.target + " " + SELECTOR_NAV_LINKS + "," + (this._config.target + " " + SELECTOR_LIST_ITEMS + ",") + (this._config.target + " " + SELECTOR_DROPDOWN_ITEMS);
  64. this._offsets = [];
  65. this._targets = [];
  66. this._activeTarget = null;
  67. this._scrollHeight = 0;
  68. $(this._scrollElement).on(EVENT_SCROLL, function (event) {
  69. return _this._process(event);
  70. });
  71. this.refresh();
  72. this._process();
  73. } // Getters
  74. var _proto = ScrollSpy.prototype;
  75. // Public
  76. _proto.refresh = function refresh() {
  77. var _this2 = this;
  78. var autoMethod = this._scrollElement === this._scrollElement.window ? METHOD_OFFSET : METHOD_POSITION;
  79. var offsetMethod = this._config.method === 'auto' ? autoMethod : this._config.method;
  80. var offsetBase = offsetMethod === METHOD_POSITION ? this._getScrollTop() : 0;
  81. this._offsets = [];
  82. this._targets = [];
  83. this._scrollHeight = this._getScrollHeight();
  84. var targets = [].slice.call(document.querySelectorAll(this._selector));
  85. targets.map(function (element) {
  86. var target;
  87. var targetSelector = Util.getSelectorFromElement(element);
  88. if (targetSelector) {
  89. target = document.querySelector(targetSelector);
  90. }
  91. if (target) {
  92. var targetBCR = target.getBoundingClientRect();
  93. if (targetBCR.width || targetBCR.height) {
  94. // TODO (fat): remove sketch reliance on jQuery position/offset
  95. return [$(target)[offsetMethod]().top + offsetBase, targetSelector];
  96. }
  97. }
  98. return null;
  99. }).filter(function (item) {
  100. return item;
  101. }).sort(function (a, b) {
  102. return a[0] - b[0];
  103. }).forEach(function (item) {
  104. _this2._offsets.push(item[0]);
  105. _this2._targets.push(item[1]);
  106. });
  107. };
  108. _proto.dispose = function dispose() {
  109. $.removeData(this._element, DATA_KEY);
  110. $(this._scrollElement).off(EVENT_KEY);
  111. this._element = null;
  112. this._scrollElement = null;
  113. this._config = null;
  114. this._selector = null;
  115. this._offsets = null;
  116. this._targets = null;
  117. this._activeTarget = null;
  118. this._scrollHeight = null;
  119. } // Private
  120. ;
  121. _proto._getConfig = function _getConfig(config) {
  122. config = _extends({}, Default, typeof config === 'object' && config ? config : {});
  123. if (typeof config.target !== 'string' && Util.isElement(config.target)) {
  124. var id = $(config.target).attr('id');
  125. if (!id) {
  126. id = Util.getUID(NAME);
  127. $(config.target).attr('id', id);
  128. }
  129. config.target = "#" + id;
  130. }
  131. Util.typeCheckConfig(NAME, config, DefaultType);
  132. return config;
  133. };
  134. _proto._getScrollTop = function _getScrollTop() {
  135. return this._scrollElement === window ? this._scrollElement.pageYOffset : this._scrollElement.scrollTop;
  136. };
  137. _proto._getScrollHeight = function _getScrollHeight() {
  138. return this._scrollElement.scrollHeight || Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);
  139. };
  140. _proto._getOffsetHeight = function _getOffsetHeight() {
  141. return this._scrollElement === window ? window.innerHeight : this._scrollElement.getBoundingClientRect().height;
  142. };
  143. _proto._process = function _process() {
  144. var scrollTop = this._getScrollTop() + this._config.offset;
  145. var scrollHeight = this._getScrollHeight();
  146. var maxScroll = this._config.offset + scrollHeight - this._getOffsetHeight();
  147. if (this._scrollHeight !== scrollHeight) {
  148. this.refresh();
  149. }
  150. if (scrollTop >= maxScroll) {
  151. var target = this._targets[this._targets.length - 1];
  152. if (this._activeTarget !== target) {
  153. this._activate(target);
  154. }
  155. return;
  156. }
  157. if (this._activeTarget && scrollTop < this._offsets[0] && this._offsets[0] > 0) {
  158. this._activeTarget = null;
  159. this._clear();
  160. return;
  161. }
  162. for (var i = this._offsets.length; i--;) {
  163. var isActiveTarget = this._activeTarget !== this._targets[i] && scrollTop >= this._offsets[i] && (typeof this._offsets[i + 1] === 'undefined' || scrollTop < this._offsets[i + 1]);
  164. if (isActiveTarget) {
  165. this._activate(this._targets[i]);
  166. }
  167. }
  168. };
  169. _proto._activate = function _activate(target) {
  170. this._activeTarget = target;
  171. this._clear();
  172. var queries = this._selector.split(',').map(function (selector) {
  173. return selector + "[data-target=\"" + target + "\"]," + selector + "[href=\"" + target + "\"]";
  174. });
  175. var $link = $([].slice.call(document.querySelectorAll(queries.join(','))));
  176. if ($link.hasClass(CLASS_NAME_DROPDOWN_ITEM)) {
  177. $link.closest(SELECTOR_DROPDOWN).find(SELECTOR_DROPDOWN_TOGGLE).addClass(CLASS_NAME_ACTIVE);
  178. $link.addClass(CLASS_NAME_ACTIVE);
  179. } else {
  180. // Set triggered link as active
  181. $link.addClass(CLASS_NAME_ACTIVE); // Set triggered links parents as active
  182. // With both <ul> and <nav> markup a parent is the previous sibling of any nav ancestor
  183. $link.parents(SELECTOR_NAV_LIST_GROUP).prev(SELECTOR_NAV_LINKS + ", " + SELECTOR_LIST_ITEMS).addClass(CLASS_NAME_ACTIVE); // Handle special case when .nav-link is inside .nav-item
  184. $link.parents(SELECTOR_NAV_LIST_GROUP).prev(SELECTOR_NAV_ITEMS).children(SELECTOR_NAV_LINKS).addClass(CLASS_NAME_ACTIVE);
  185. }
  186. $(this._scrollElement).trigger(EVENT_ACTIVATE, {
  187. relatedTarget: target
  188. });
  189. };
  190. _proto._clear = function _clear() {
  191. [].slice.call(document.querySelectorAll(this._selector)).filter(function (node) {
  192. return node.classList.contains(CLASS_NAME_ACTIVE);
  193. }).forEach(function (node) {
  194. return node.classList.remove(CLASS_NAME_ACTIVE);
  195. });
  196. } // Static
  197. ;
  198. ScrollSpy._jQueryInterface = function _jQueryInterface(config) {
  199. return this.each(function () {
  200. var data = $(this).data(DATA_KEY);
  201. var _config = typeof config === 'object' && config;
  202. if (!data) {
  203. data = new ScrollSpy(this, _config);
  204. $(this).data(DATA_KEY, data);
  205. }
  206. if (typeof config === 'string') {
  207. if (typeof data[config] === 'undefined') {
  208. throw new TypeError("No method named \"" + config + "\"");
  209. }
  210. data[config]();
  211. }
  212. });
  213. };
  214. _createClass(ScrollSpy, null, [{
  215. key: "VERSION",
  216. get: function get() {
  217. return VERSION;
  218. }
  219. }, {
  220. key: "Default",
  221. get: function get() {
  222. return Default;
  223. }
  224. }]);
  225. return ScrollSpy;
  226. }();
  227. /**
  228. * ------------------------------------------------------------------------
  229. * Data Api implementation
  230. * ------------------------------------------------------------------------
  231. */
  232. $(window).on(EVENT_LOAD_DATA_API, function () {
  233. var scrollSpys = [].slice.call(document.querySelectorAll(SELECTOR_DATA_SPY));
  234. var scrollSpysLength = scrollSpys.length;
  235. for (var i = scrollSpysLength; i--;) {
  236. var $spy = $(scrollSpys[i]);
  237. ScrollSpy._jQueryInterface.call($spy, $spy.data());
  238. }
  239. });
  240. /**
  241. * ------------------------------------------------------------------------
  242. * jQuery
  243. * ------------------------------------------------------------------------
  244. */
  245. $.fn[NAME] = ScrollSpy._jQueryInterface;
  246. $.fn[NAME].Constructor = ScrollSpy;
  247. $.fn[NAME].noConflict = function () {
  248. $.fn[NAME] = JQUERY_NO_CONFLICT;
  249. return ScrollSpy._jQueryInterface;
  250. };
  251. return ScrollSpy;
  252. })));
  253. //# sourceMappingURL=scrollspy.js.map