scrollspy.js 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
  2. var _createClass = function () { 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); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
  3. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  4. /**
  5. * --------------------------------------------------------------------------
  6. * Bootstrap (v4.0.0-alpha.4): scrollspy.js
  7. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
  8. * --------------------------------------------------------------------------
  9. */
  10. var ScrollSpy = function ($) {
  11. /**
  12. * ------------------------------------------------------------------------
  13. * Constants
  14. * ------------------------------------------------------------------------
  15. */
  16. var NAME = 'scrollspy';
  17. var VERSION = '4.0.0-alpha.4';
  18. var DATA_KEY = 'bs.scrollspy';
  19. var EVENT_KEY = '.' + DATA_KEY;
  20. var DATA_API_KEY = '.data-api';
  21. var JQUERY_NO_CONFLICT = $.fn[NAME];
  22. var Default = {
  23. offset: 10,
  24. method: 'auto',
  25. target: ''
  26. };
  27. var DefaultType = {
  28. offset: 'number',
  29. method: 'string',
  30. target: '(string|element)'
  31. };
  32. var Event = {
  33. ACTIVATE: 'activate' + EVENT_KEY,
  34. SCROLL: 'scroll' + EVENT_KEY,
  35. LOAD_DATA_API: 'load' + EVENT_KEY + DATA_API_KEY
  36. };
  37. var ClassName = {
  38. DROPDOWN_ITEM: 'dropdown-item',
  39. DROPDOWN_MENU: 'dropdown-menu',
  40. NAV_LINK: 'nav-link',
  41. NAV: 'nav',
  42. ACTIVE: 'active'
  43. };
  44. var Selector = {
  45. DATA_SPY: '[data-spy="scroll"]',
  46. ACTIVE: '.active',
  47. LIST_ITEM: '.list-item',
  48. LI: 'li',
  49. LI_DROPDOWN: 'li.dropdown',
  50. NAV_LINKS: '.nav-link',
  51. DROPDOWN: '.dropdown',
  52. DROPDOWN_ITEMS: '.dropdown-item',
  53. DROPDOWN_TOGGLE: '.dropdown-toggle'
  54. };
  55. var OffsetMethod = {
  56. OFFSET: 'offset',
  57. POSITION: 'position'
  58. };
  59. /**
  60. * ------------------------------------------------------------------------
  61. * Class Definition
  62. * ------------------------------------------------------------------------
  63. */
  64. var ScrollSpy = function () {
  65. function ScrollSpy(element, config) {
  66. _classCallCheck(this, ScrollSpy);
  67. this._element = element;
  68. this._scrollElement = element.tagName === 'BODY' ? window : element;
  69. this._config = this._getConfig(config);
  70. this._selector = this._config.target + ' ' + Selector.NAV_LINKS + ',' + (this._config.target + ' ' + Selector.DROPDOWN_ITEMS);
  71. this._offsets = [];
  72. this._targets = [];
  73. this._activeTarget = null;
  74. this._scrollHeight = 0;
  75. $(this._scrollElement).on(Event.SCROLL, $.proxy(this._process, this));
  76. this.refresh();
  77. this._process();
  78. }
  79. // getters
  80. // public
  81. ScrollSpy.prototype.refresh = function refresh() {
  82. var _this = this;
  83. var autoMethod = this._scrollElement !== this._scrollElement.window ? OffsetMethod.POSITION : OffsetMethod.OFFSET;
  84. var offsetMethod = this._config.method === 'auto' ? autoMethod : this._config.method;
  85. var offsetBase = offsetMethod === OffsetMethod.POSITION ? this._getScrollTop() : 0;
  86. this._offsets = [];
  87. this._targets = [];
  88. this._scrollHeight = this._getScrollHeight();
  89. var targets = $.makeArray($(this._selector));
  90. targets.map(function (element) {
  91. var target = void 0;
  92. var targetSelector = Util.getSelectorFromElement(element);
  93. if (targetSelector) {
  94. target = $(targetSelector)[0];
  95. }
  96. if (target && (target.offsetWidth || target.offsetHeight)) {
  97. // todo (fat): remove sketch reliance on jQuery position/offset
  98. return [$(target)[offsetMethod]().top + offsetBase, targetSelector];
  99. }
  100. return null;
  101. }).filter(function (item) {
  102. return item;
  103. }).sort(function (a, b) {
  104. return a[0] - b[0];
  105. }).forEach(function (item) {
  106. _this._offsets.push(item[0]);
  107. _this._targets.push(item[1]);
  108. });
  109. };
  110. ScrollSpy.prototype.dispose = function dispose() {
  111. $.removeData(this._element, DATA_KEY);
  112. $(this._scrollElement).off(EVENT_KEY);
  113. this._element = null;
  114. this._scrollElement = null;
  115. this._config = null;
  116. this._selector = null;
  117. this._offsets = null;
  118. this._targets = null;
  119. this._activeTarget = null;
  120. this._scrollHeight = null;
  121. };
  122. // private
  123. ScrollSpy.prototype._getConfig = function _getConfig(config) {
  124. config = $.extend({}, Default, config);
  125. if (typeof config.target !== 'string') {
  126. var id = $(config.target).attr('id');
  127. if (!id) {
  128. id = Util.getUID(NAME);
  129. $(config.target).attr('id', id);
  130. }
  131. config.target = '#' + id;
  132. }
  133. Util.typeCheckConfig(NAME, config, DefaultType);
  134. return config;
  135. };
  136. ScrollSpy.prototype._getScrollTop = function _getScrollTop() {
  137. return this._scrollElement === window ? this._scrollElement.scrollY : this._scrollElement.scrollTop;
  138. };
  139. ScrollSpy.prototype._getScrollHeight = function _getScrollHeight() {
  140. return this._scrollElement.scrollHeight || Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);
  141. };
  142. ScrollSpy.prototype._process = function _process() {
  143. var scrollTop = this._getScrollTop() + this._config.offset;
  144. var scrollHeight = this._getScrollHeight();
  145. var maxScroll = this._config.offset + scrollHeight - this._scrollElement.offsetHeight;
  146. if (this._scrollHeight !== scrollHeight) {
  147. this.refresh();
  148. }
  149. if (scrollTop >= maxScroll) {
  150. var target = this._targets[this._targets.length - 1];
  151. if (this._activeTarget !== target) {
  152. this._activate(target);
  153. }
  154. }
  155. if (this._activeTarget && scrollTop < this._offsets[0]) {
  156. this._activeTarget = null;
  157. this._clear();
  158. return;
  159. }
  160. for (var i = this._offsets.length; i--;) {
  161. var isActiveTarget = this._activeTarget !== this._targets[i] && scrollTop >= this._offsets[i] && (this._offsets[i + 1] === undefined || scrollTop < this._offsets[i + 1]);
  162. if (isActiveTarget) {
  163. this._activate(this._targets[i]);
  164. }
  165. }
  166. };
  167. ScrollSpy.prototype._activate = function _activate(target) {
  168. this._activeTarget = target;
  169. this._clear();
  170. var queries = this._selector.split(',');
  171. queries = queries.map(function (selector) {
  172. return selector + '[data-target="' + target + '"],' + (selector + '[href="' + target + '"]');
  173. });
  174. var $link = $(queries.join(','));
  175. if ($link.hasClass(ClassName.DROPDOWN_ITEM)) {
  176. $link.closest(Selector.DROPDOWN).find(Selector.DROPDOWN_TOGGLE).addClass(ClassName.ACTIVE);
  177. $link.addClass(ClassName.ACTIVE);
  178. } else {
  179. // todo (fat) this is kinda sus...
  180. // recursively add actives to tested nav-links
  181. $link.parents(Selector.LI).find(Selector.NAV_LINKS).addClass(ClassName.ACTIVE);
  182. }
  183. $(this._scrollElement).trigger(Event.ACTIVATE, {
  184. relatedTarget: target
  185. });
  186. };
  187. ScrollSpy.prototype._clear = function _clear() {
  188. $(this._selector).filter(Selector.ACTIVE).removeClass(ClassName.ACTIVE);
  189. };
  190. // static
  191. ScrollSpy._jQueryInterface = function _jQueryInterface(config) {
  192. return this.each(function () {
  193. var data = $(this).data(DATA_KEY);
  194. var _config = (typeof config === 'undefined' ? 'undefined' : _typeof(config)) === 'object' && config || null;
  195. if (!data) {
  196. data = new ScrollSpy(this, _config);
  197. $(this).data(DATA_KEY, data);
  198. }
  199. if (typeof config === 'string') {
  200. if (data[config] === undefined) {
  201. throw new Error('No method named "' + config + '"');
  202. }
  203. data[config]();
  204. }
  205. });
  206. };
  207. _createClass(ScrollSpy, null, [{
  208. key: 'VERSION',
  209. get: function get() {
  210. return VERSION;
  211. }
  212. }, {
  213. key: 'Default',
  214. get: function get() {
  215. return Default;
  216. }
  217. }]);
  218. return ScrollSpy;
  219. }();
  220. /**
  221. * ------------------------------------------------------------------------
  222. * Data Api implementation
  223. * ------------------------------------------------------------------------
  224. */
  225. $(window).on(Event.LOAD_DATA_API, function () {
  226. var scrollSpys = $.makeArray($(Selector.DATA_SPY));
  227. for (var i = scrollSpys.length; i--;) {
  228. var $spy = $(scrollSpys[i]);
  229. ScrollSpy._jQueryInterface.call($spy, $spy.data());
  230. }
  231. });
  232. /**
  233. * ------------------------------------------------------------------------
  234. * jQuery
  235. * ------------------------------------------------------------------------
  236. */
  237. $.fn[NAME] = ScrollSpy._jQueryInterface;
  238. $.fn[NAME].Constructor = ScrollSpy;
  239. $.fn[NAME].noConflict = function () {
  240. $.fn[NAME] = JQUERY_NO_CONFLICT;
  241. return ScrollSpy._jQueryInterface;
  242. };
  243. return ScrollSpy;
  244. }(jQuery);
  245. //# sourceMappingURL=scrollspy.js.map