diff options
| author | fat <[email protected]> | 2015-05-11 12:05:35 -0700 |
|---|---|---|
| committer | fat <[email protected]> | 2015-05-11 12:05:35 -0700 |
| commit | 2d91494d96f8161f1bcce6589081880b72706154 (patch) | |
| tree | 7b796ddd84d13abd0e66e388adcfc98cbb7cccde /js/src | |
| parent | ca9c850ebbfc880dc5021b451ffc9fa12184ff87 (diff) | |
| download | bootstrap-2d91494d96f8161f1bcce6589081880b72706154.tar.xz bootstrap-2d91494d96f8161f1bcce6589081880b72706154.zip | |
scrollspy es6
Diffstat (limited to 'js/src')
| -rw-r--r-- | js/src/scrollspy.js | 275 |
1 files changed, 275 insertions, 0 deletions
diff --git a/js/src/scrollspy.js b/js/src/scrollspy.js new file mode 100644 index 000000000..985da708d --- /dev/null +++ b/js/src/scrollspy.js @@ -0,0 +1,275 @@ +import Util from './util' + + +/** + * -------------------------------------------------------------------------- + * Bootstrap (v4.0.0): scrollspy.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * -------------------------------------------------------------------------- + */ + +const ScrollSpy = (($) => { + + + /** + * ------------------------------------------------------------------------ + * Constants + * ------------------------------------------------------------------------ + */ + + const NAME = 'scrollspy' + const VERSION = '4.0.0' + const DATA_KEY = 'bs.scrollspy' + const JQUERY_NO_CONFLICT = $.fn[NAME] + const TRANSITION_DURATION = 150 + + const Defaults = { + offset : 10 + } + + const Event = { + ACTIVATE : 'activate.bs.scrollspy', + SCROLL : 'scroll.bs.scrollspy', + LOAD : 'load.bs.scrollspy.data-api' + } + + const ClassName = { + DROPDOWN_MENU : 'dropdown-menu', + ACTIVE : 'active' + } + + const Selector = { + DATA_SPY : '[data-spy="scroll"]', + ACTIVE : '.active', + LI_DROPDOWN : 'li.dropdown', + LI : 'li' + } + + + /** + * ------------------------------------------------------------------------ + * Class Definition + * ------------------------------------------------------------------------ + */ + + class ScrollSpy { + + constructor(element, config) { + this._scrollElement = element.tagName === 'BODY' ? window : element + this._config = $.extend({}, Defaults, config) + this._selector = `${this._config.target || ''} .nav li > a` + this._offsets = [] + this._targets = [] + this._activeTarget = null + this._scrollHeight = 0 + + $(this._scrollElement).on(Event.SCROLL, this._process.bind(this)) + + this.refresh() + this._process() + } + + + // getters + + static get VERSION() { + return VERSION + } + + static get Default() { + return Default + } + + + // public + + refresh() { + let offsetMethod = 'offset' + let offsetBase = 0 + + if (this._scrollElement !== this._scrollElement.window) { + offsetMethod = 'position' + offsetBase = this._getScrollTop() + } + + this._offsets = [] + this._targets = [] + + this._scrollHeight = this._getScrollHeight() + + let targets = $.makeArray($(this._selector)) + + targets + .map((element) => { + let target + let targetSelector = Util.getSelectorFromElement(element) + + if (targetSelector) { + target = $(targetSelector)[0] + } + + if (target && (target.offsetWidth || target.offsetHeight)) { + // todo (fat): remove sketch reliance on jQuery position/offset + return [ + $(target)[offsetMethod]().top + offsetBase, + targetSelector + ] + } + }) + .filter((item) => item) + .sort((a, b) => a[0] - b[0]) + .forEach((item) => { + this._offsets.push(item[0]) + this._targets.push(item[1]) + }) + } + + + // private + + _getScrollTop() { + return this._scrollElement === window ? + this._scrollElement.scrollY : this._scrollElement.scrollTop + } + + _getScrollHeight() { + return this._scrollElement.scrollHeight || Math.max( + document.body.scrollHeight, + document.documentElement.scrollHeight + ) + } + + _process() { + let scrollTop = this._getScrollTop() + this._config.offset + let scrollHeight = this._getScrollHeight() + let maxScroll = this._config.offset + + scrollHeight + - this._scrollElement.offsetHeight + + if (this._scrollHeight !== scrollHeight) { + this.refresh() + } + + if (scrollTop >= maxScroll) { + let target = this._targets[this._targets.length - 1] + + if (this._activeTarget !== target) { + this._activate(target) + } + } + + if (this._activeTarget && scrollTop < this._offsets[0]) { + this._activeTarget = null + this._clear() + return + } + + for (let i = this._offsets.length; i--;) { + let isActiveTarget = this._activeTarget !== this._targets[i] + && scrollTop >= this._offsets[i] + && (this._offsets[i + 1] === undefined || + scrollTop < this._offsets[i + 1]) + + if (isActiveTarget) { + this._activate(this._targets[i]) + } + } + } + + _activate(target) { + this._activeTarget = target + + this._clear() + + let selector = + `${this._selector}[data-target="${target}"],` + + `${this._selector}[href="${target}"]` + + // todo (fat): getting all the raw li's up the tree is not great. + let parentListItems = $(selector).parents(Selector.LI) + + for (let i = parentListItems.length; i--;) { + $(parentListItems[i]).addClass(ClassName.ACTIVE) + + let itemParent = parentListItems[i].parentNode + + if (itemParent && $(itemParent).hasClass(ClassName.DROPDOWN_MENU)) { + let closestDropdown = $(itemParent) + .closest(Selector.LI_DROPDOWN)[0] + $(closestDropdown).addClass(ClassName.ACTIVE) + } + } + + $(this._scrollElement).trigger(Event.ACTIVATE, { + relatedTarget: target + }) + } + + _clear() { + let activeParents = $(this._selector).parentsUntil( + this._config.target, + Selector.ACTIVE + ) + + for (let i = activeParents.length; i--;) { + $(activeParents[i]).removeClass(ClassName.ACTIVE) + } + } + + + // static + + static _jQueryInterface(config) { + return this.each(function () { + let data = $(this).data(DATA_KEY) + let _config = typeof config === 'object' && config || null + + if (!data) { + data = new ScrollSpy(this, _config) + $(this).data(DATA_KEY, data) + } + + if (typeof config === 'string') { + data[config]() + } + }) + } + + + } + + + /** + * ------------------------------------------------------------------------ + * Data Api implementation + * ------------------------------------------------------------------------ + */ + + $(window).on(Event.LOAD, function () { + let scrollSpys = $.makeArray($(Selector.DATA_SPY)) + + for (let i = scrollSpys.length; i--;) { + let $spy = $(scrollSpys[i]) + ScrollSpy._jQueryInterface.call($spy, $spy.data()) + } + }) + + + /** + * ------------------------------------------------------------------------ + * jQuery + * ------------------------------------------------------------------------ + */ + + $.fn[NAME] = ScrollSpy._jQueryInterface + $.fn[NAME].Constructor = ScrollSpy + $.fn[NAME].noConflict = function () { + $.fn[NAME] = JQUERY_NO_CONFLICT + return ScrollSpy._jQueryInterface + } + + return ScrollSpy + +})(jQuery) + +export default ScrollSpy |
