scrollspy.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. /*!
  2. * Bootstrap scrollspy.js v4.3.1 (https://getbootstrap.com/)
  3. * Copyright 2011-2019 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
  4. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/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 = global || self, global.ScrollSpy = factory(global.jQuery, global.Util));
  10. }(this, function ($, Util) { 'use strict';
  11. $ = $ && $.hasOwnProperty('default') ? $['default'] : $;
  12. Util = Util && Util.hasOwnProperty('default') ? Util['default'] : Util;
  13. function _defineProperties(target, props) {
  14. for (var i = 0; i < props.length; i++) {
  15. var descriptor = props[i];
  16. descriptor.enumerable = descriptor.enumerable || false;
  17. descriptor.configurable = true;
  18. if ("value" in descriptor) descriptor.writable = true;
  19. Object.defineProperty(target, descriptor.key, descriptor);
  20. }
  21. }
  22. function _createClass(Constructor, protoProps, staticProps) {
  23. if (protoProps) _defineProperties(Constructor.prototype, protoProps);
  24. if (staticProps) _defineProperties(Constructor, staticProps);
  25. return Constructor;
  26. }
  27. function _defineProperty(obj, key, value) {
  28. if (key in obj) {
  29. Object.defineProperty(obj, key, {
  30. value: value,
  31. enumerable: true,
  32. configurable: true,
  33. writable: true
  34. });
  35. } else {
  36. obj[key] = value;
  37. }
  38. return obj;
  39. }
  40. function _objectSpread(target) {
  41. for (var i = 1; i < arguments.length; i++) {
  42. var source = arguments[i] != null ? arguments[i] : {};
  43. var ownKeys = Object.keys(source);
  44. if (typeof Object.getOwnPropertySymbols === 'function') {
  45. ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) {
  46. return Object.getOwnPropertyDescriptor(source, sym).enumerable;
  47. }));
  48. }
  49. ownKeys.forEach(function (key) {
  50. _defineProperty(target, key, source[key]);
  51. });
  52. }
  53. return target;
  54. }
  55. /**
  56. * ------------------------------------------------------------------------
  57. * Constants
  58. * ------------------------------------------------------------------------
  59. */
  60. var NAME = 'scrollspy';
  61. var VERSION = '4.3.1';
  62. var DATA_KEY = 'bs.scrollspy';
  63. var EVENT_KEY = "." + DATA_KEY;
  64. var DATA_API_KEY = '.data-api';
  65. var JQUERY_NO_CONFLICT = $.fn[NAME];
  66. var Default = {
  67. offset: 10,
  68. method: 'auto',
  69. target: ''
  70. };
  71. var DefaultType = {
  72. offset: 'number',
  73. method: 'string',
  74. target: '(string|element)'
  75. };
  76. var Event = {
  77. ACTIVATE: "activate" + EVENT_KEY,
  78. SCROLL: "scroll" + EVENT_KEY,
  79. LOAD_DATA_API: "load" + EVENT_KEY + DATA_API_KEY
  80. };
  81. var ClassName = {
  82. DROPDOWN_ITEM: 'dropdown-item',
  83. DROPDOWN_MENU: 'dropdown-menu',
  84. ACTIVE: 'active'
  85. };
  86. var Selector = {
  87. DATA_SPY: '[data-spy="scroll"]',
  88. ACTIVE: '.active',
  89. NAV_LIST_GROUP: '.nav, .list-group',
  90. NAV_LINKS: '.nav-link',
  91. NAV_ITEMS: '.nav-item',
  92. LIST_ITEMS: '.list-group-item',
  93. DROPDOWN: '.dropdown',
  94. DROPDOWN_ITEMS: '.dropdown-item',
  95. DROPDOWN_TOGGLE: '.dropdown-toggle'
  96. };
  97. var OffsetMethod = {
  98. OFFSET: 'offset',
  99. POSITION: 'position'
  100. /**
  101. * ------------------------------------------------------------------------
  102. * Class Definition
  103. * ------------------------------------------------------------------------
  104. */
  105. };
  106. var ScrollSpy =
  107. /*#__PURE__*/
  108. function () {
  109. function ScrollSpy(element, config) {
  110. var _this = this;
  111. this._element = element;
  112. this._scrollElement = element.tagName === 'BODY' ? window : element;
  113. this._config = this._getConfig(config);
  114. this._selector = this._config.target + " " + Selector.NAV_LINKS + "," + (this._config.target + " " + Selector.LIST_ITEMS + ",") + (this._config.target + " " + Selector.DROPDOWN_ITEMS);
  115. this._offsets = [];
  116. this._targets = [];
  117. this._activeTarget = null;
  118. this._scrollHeight = 0;
  119. $(this._scrollElement).on(Event.SCROLL, function (event) {
  120. return _this._process(event);
  121. });
  122. this.refresh();
  123. this._process();
  124. } // Getters
  125. var _proto = ScrollSpy.prototype;
  126. // Public
  127. _proto.refresh = function refresh() {
  128. var _this2 = this;
  129. var autoMethod = this._scrollElement === this._scrollElement.window ? OffsetMethod.OFFSET : OffsetMethod.POSITION;
  130. var offsetMethod = this._config.method === 'auto' ? autoMethod : this._config.method;
  131. var offsetBase = offsetMethod === OffsetMethod.POSITION ? this._getScrollTop() : 0;
  132. this._offsets = [];
  133. this._targets = [];
  134. this._scrollHeight = this._getScrollHeight();
  135. var targets = [].slice.call(document.querySelectorAll(this._selector));
  136. targets.map(function (element) {
  137. var target;
  138. var targetSelector = Util.getSelectorFromElement(element);
  139. if (targetSelector) {
  140. target = document.querySelector(targetSelector);
  141. }
  142. if (target) {
  143. var targetBCR = target.getBoundingClientRect();
  144. if (targetBCR.width || targetBCR.height) {
  145. // TODO (fat): remove sketch reliance on jQuery position/offset
  146. return [$(target)[offsetMethod]().top + offsetBase, targetSelector];
  147. }
  148. }
  149. return null;
  150. }).filter(function (item) {
  151. return item;
  152. }).sort(function (a, b) {
  153. return a[0] - b[0];
  154. }).forEach(function (item) {
  155. _this2._offsets.push(item[0]);
  156. _this2._targets.push(item[1]);
  157. });
  158. };
  159. _proto.dispose = function dispose() {
  160. $.removeData(this._element, DATA_KEY);
  161. $(this._scrollElement).off(EVENT_KEY);
  162. this._element = null;
  163. this._scrollElement = null;
  164. this._config = null;
  165. this._selector = null;
  166. this._offsets = null;
  167. this._targets = null;
  168. this._activeTarget = null;
  169. this._scrollHeight = null;
  170. } // Private
  171. ;
  172. _proto._getConfig = function _getConfig(config) {
  173. config = _objectSpread({}, Default, typeof config === 'object' && config ? config : {});
  174. if (typeof config.target !== 'string') {
  175. var id = $(config.target).attr('id');
  176. if (!id) {
  177. id = Util.getUID(NAME);
  178. $(config.target).attr('id', id);
  179. }
  180. config.target = "#" + id;
  181. }
  182. Util.typeCheckConfig(NAME, config, DefaultType);
  183. return config;
  184. };
  185. _proto._getScrollTop = function _getScrollTop() {
  186. return this._scrollElement === window ? this._scrollElement.pageYOffset : this._scrollElement.scrollTop;
  187. };
  188. _proto._getScrollHeight = function _getScrollHeight() {
  189. return this._scrollElement.scrollHeight || Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);
  190. };
  191. _proto._getOffsetHeight = function _getOffsetHeight() {
  192. return this._scrollElement === window ? window.innerHeight : this._scrollElement.getBoundingClientRect().height;
  193. };
  194. _proto._process = function _process() {
  195. var scrollTop = this._getScrollTop() + this._config.offset;
  196. var scrollHeight = this._getScrollHeight();
  197. var maxScroll = this._config.offset + scrollHeight - this._getOffsetHeight();
  198. if (this._scrollHeight !== scrollHeight) {
  199. this.refresh();
  200. }
  201. if (scrollTop >= maxScroll) {
  202. var target = this._targets[this._targets.length - 1];
  203. if (this._activeTarget !== target) {
  204. this._activate(target);
  205. }
  206. return;
  207. }
  208. if (this._activeTarget && scrollTop < this._offsets[0] && this._offsets[0] > 0) {
  209. this._activeTarget = null;
  210. this._clear();
  211. return;
  212. }
  213. var offsetLength = this._offsets.length;
  214. for (var i = offsetLength; i--;) {
  215. var isActiveTarget = this._activeTarget !== this._targets[i] && scrollTop >= this._offsets[i] && (typeof this._offsets[i + 1] === 'undefined' || scrollTop < this._offsets[i + 1]);
  216. if (isActiveTarget) {
  217. this._activate(this._targets[i]);
  218. }
  219. }
  220. };
  221. _proto._activate = function _activate(target) {
  222. this._activeTarget = target;
  223. this._clear();
  224. var queries = this._selector.split(',').map(function (selector) {
  225. return selector + "[data-target=\"" + target + "\"]," + selector + "[href=\"" + target + "\"]";
  226. });
  227. var $link = $([].slice.call(document.querySelectorAll(queries.join(','))));
  228. if ($link.hasClass(ClassName.DROPDOWN_ITEM)) {
  229. $link.closest(Selector.DROPDOWN).find(Selector.DROPDOWN_TOGGLE).addClass(ClassName.ACTIVE);
  230. $link.addClass(ClassName.ACTIVE);
  231. } else {
  232. // Set triggered link as active
  233. $link.addClass(ClassName.ACTIVE); // Set triggered links parents as active
  234. // With both <ul> and <nav> markup a parent is the previous sibling of any nav ancestor
  235. $link.parents(Selector.NAV_LIST_GROUP).prev(Selector.NAV_LINKS + ", " + Selector.LIST_ITEMS).addClass(ClassName.ACTIVE); // Handle special case when .nav-link is inside .nav-item
  236. $link.parents(Selector.NAV_LIST_GROUP).prev(Selector.NAV_ITEMS).children(Selector.NAV_LINKS).addClass(ClassName.ACTIVE);
  237. }
  238. $(this._scrollElement).trigger(Event.ACTIVATE, {
  239. relatedTarget: target
  240. });
  241. };
  242. _proto._clear = function _clear() {
  243. [].slice.call(document.querySelectorAll(this._selector)).filter(function (node) {
  244. return node.classList.contains(ClassName.ACTIVE);
  245. }).forEach(function (node) {
  246. return node.classList.remove(ClassName.ACTIVE);
  247. });
  248. } // Static
  249. ;
  250. ScrollSpy._jQueryInterface = function _jQueryInterface(config) {
  251. return this.each(function () {
  252. var data = $(this).data(DATA_KEY);
  253. var _config = typeof config === 'object' && config;
  254. if (!data) {
  255. data = new ScrollSpy(this, _config);
  256. $(this).data(DATA_KEY, data);
  257. }
  258. if (typeof config === 'string') {
  259. if (typeof data[config] === 'undefined') {
  260. throw new TypeError("No method named \"" + config + "\"");
  261. }
  262. data[config]();
  263. }
  264. });
  265. };
  266. _createClass(ScrollSpy, null, [{
  267. key: "VERSION",
  268. get: function get() {
  269. return VERSION;
  270. }
  271. }, {
  272. key: "Default",
  273. get: function get() {
  274. return Default;
  275. }
  276. }]);
  277. return ScrollSpy;
  278. }();
  279. /**
  280. * ------------------------------------------------------------------------
  281. * Data Api implementation
  282. * ------------------------------------------------------------------------
  283. */
  284. $(window).on(Event.LOAD_DATA_API, function () {
  285. var scrollSpys = [].slice.call(document.querySelectorAll(Selector.DATA_SPY));
  286. var scrollSpysLength = scrollSpys.length;
  287. for (var i = scrollSpysLength; i--;) {
  288. var $spy = $(scrollSpys[i]);
  289. ScrollSpy._jQueryInterface.call($spy, $spy.data());
  290. }
  291. });
  292. /**
  293. * ------------------------------------------------------------------------
  294. * jQuery
  295. * ------------------------------------------------------------------------
  296. */
  297. $.fn[NAME] = ScrollSpy._jQueryInterface;
  298. $.fn[NAME].Constructor = ScrollSpy;
  299. $.fn[NAME].noConflict = function () {
  300. $.fn[NAME] = JQUERY_NO_CONFLICT;
  301. return ScrollSpy._jQueryInterface;
  302. };
  303. return ScrollSpy;
  304. }));
  305. //# sourceMappingURL=scrollspy.js.map