diff options
author | Johann-S <johann.servoire@gmail.com> | 2017-09-15 14:13:14 +0300 |
---|---|---|
committer | XhmikosR <xhmikosr@gmail.com> | 2019-02-20 23:05:45 +0300 |
commit | 69e4d4f3ac897870e8186e22b19c5adf6043f3d4 (patch) | |
tree | aa85e49caa6f07570682657e3d82322fd579bb09 /js/src/collapse.js | |
parent | 53ca76ebc0fa2737e7344efedabb28b2a0d6be53 (diff) |
Rewrite Collapse without jQuery
Diffstat (limited to 'js/src/collapse.js')
-rw-r--r-- | js/src/collapse.js | 211 |
1 files changed, 115 insertions, 96 deletions
diff --git a/js/src/collapse.js b/js/src/collapse.js index a6b540de9e..9c7ff92776 100644 --- a/js/src/collapse.js +++ b/js/src/collapse.js @@ -5,7 +5,9 @@ * -------------------------------------------------------------------------- */ -import $ from 'jquery' +import Data from './dom/data' +import EventHandler from './dom/eventHandler' +import SelectorEngine from './dom/selectorEngine' import Util from './util' /** @@ -19,7 +21,6 @@ const VERSION = '4.3.1' const DATA_KEY = 'bs.collapse' const EVENT_KEY = `.${DATA_KEY}` const DATA_API_KEY = '.data-api' -const JQUERY_NO_CONFLICT = $.fn[NAME] const Default = { toggle : true, @@ -67,19 +68,19 @@ class Collapse { this._isTransitioning = false this._element = element this._config = this._getConfig(config) - this._triggerArray = [].slice.call(document.querySelectorAll( + this._triggerArray = Util.makeArray(SelectorEngine.find( `[data-toggle="collapse"][href="#${element.id}"],` + `[data-toggle="collapse"][data-target="#${element.id}"]` )) - const toggleList = [].slice.call(document.querySelectorAll(Selector.DATA_TOGGLE)) + const toggleList = Util.makeArray(document.querySelectorAll(Selector.DATA_TOGGLE)) for (let i = 0, len = toggleList.length; i < len; i++) { const elem = toggleList[i] const selector = Util.getSelectorFromElement(elem) - const filterElement = [].slice.call(document.querySelectorAll(selector)) + const filterElement = Util.makeArray(document.querySelectorAll(selector)) .filter((foundElem) => foundElem === element) - if (selector !== null && filterElement.length > 0) { + if (selector !== null && filterElement.length) { this._selector = selector this._triggerArray.push(elem) } @@ -109,7 +110,7 @@ class Collapse { // Public toggle() { - if ($(this._element).hasClass(ClassName.SHOW)) { + if (this._element.classList.contains(ClassName.SHOW)) { this.hide() } else { this.show() @@ -118,7 +119,7 @@ class Collapse { show() { if (this._isTransitioning || - $(this._element).hasClass(ClassName.SHOW)) { + this._element.classList.contains(ClassName.SHOW)) { return } @@ -126,7 +127,7 @@ class Collapse { let activesData if (this._parent) { - actives = [].slice.call(this._parent.querySelectorAll(Selector.ACTIVES)) + actives = Util.makeArray(this._parent.querySelectorAll(Selector.ACTIVES)) .filter((elem) => { if (typeof this._config.parent === 'string') { return elem.getAttribute('data-parent') === this._config.parent @@ -141,74 +142,70 @@ class Collapse { } if (actives) { - activesData = $(actives).not(this._selector).data(DATA_KEY) + activesData = Data.getData(actives[0], DATA_KEY) if (activesData && activesData._isTransitioning) { return } } - const startEvent = $.Event(Event.SHOW) - $(this._element).trigger(startEvent) - if (startEvent.isDefaultPrevented()) { + const startEvent = EventHandler.trigger(this._element, Event.SHOW) + if (startEvent.defaultPrevented) { return } if (actives) { - Collapse._jQueryInterface.call($(actives).not(this._selector), 'hide') + actives.forEach((elemActive) => Collapse._collapseInterface(elemActive, 'hide')) if (!activesData) { - $(actives).data(DATA_KEY, null) + Data.setData(actives[0], DATA_KEY, null) } } const dimension = this._getDimension() - $(this._element) - .removeClass(ClassName.COLLAPSE) - .addClass(ClassName.COLLAPSING) + this._element.classList.remove(ClassName.COLLAPSE) + this._element.classList.add(ClassName.COLLAPSING) this._element.style[dimension] = 0 if (this._triggerArray.length) { - $(this._triggerArray) - .removeClass(ClassName.COLLAPSED) - .attr('aria-expanded', true) + this._triggerArray.forEach((element) => { + element.classList.remove(ClassName.COLLAPSED) + element.setAttribute('aria-expanded', true) + }) } this.setTransitioning(true) const complete = () => { - $(this._element) - .removeClass(ClassName.COLLAPSING) - .addClass(ClassName.COLLAPSE) - .addClass(ClassName.SHOW) + this._element.classList.remove(ClassName.COLLAPSING) + this._element.classList.add(ClassName.COLLAPSE) + this._element.classList.add(ClassName.SHOW) this._element.style[dimension] = '' this.setTransitioning(false) - $(this._element).trigger(Event.SHOWN) + EventHandler.trigger(this._element, Event.SHOWN) } const capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1) const scrollSize = `scroll${capitalizedDimension}` const transitionDuration = Util.getTransitionDurationFromElement(this._element) - $(this._element) - .one(Util.TRANSITION_END, complete) + EventHandler.one(this._element, Util.TRANSITION_END, complete) - Util.emulateTransitionEnd(transitionDuration) + Util.emulateTransitionEnd(this._element, transitionDuration) this._element.style[dimension] = `${this._element[scrollSize]}px` } hide() { if (this._isTransitioning || - !$(this._element).hasClass(ClassName.SHOW)) { + !this._element.classList.contains(ClassName.SHOW)) { return } - const startEvent = $.Event(Event.HIDE) - $(this._element).trigger(startEvent) - if (startEvent.isDefaultPrevented()) { + const startEvent = EventHandler.trigger(this._element, Event.HIDE) + if (startEvent.defaultPrevented) { return } @@ -218,10 +215,9 @@ class Collapse { Util.reflow(this._element) - $(this._element) - .addClass(ClassName.COLLAPSING) - .removeClass(ClassName.COLLAPSE) - .removeClass(ClassName.SHOW) + this._element.classList.add(ClassName.COLLAPSING) + this._element.classList.remove(ClassName.COLLAPSE) + this._element.classList.remove(ClassName.SHOW) const triggerArrayLength = this._triggerArray.length if (triggerArrayLength > 0) { @@ -230,10 +226,11 @@ class Collapse { const selector = Util.getSelectorFromElement(trigger) if (selector !== null) { - const $elem = $([].slice.call(document.querySelectorAll(selector))) - if (!$elem.hasClass(ClassName.SHOW)) { - $(trigger).addClass(ClassName.COLLAPSED) - .attr('aria-expanded', false) + const elem = SelectorEngine.findOne(selector) + + if (!elem.classList.contains(ClassName.SHOW)) { + trigger.classList.add(ClassName.COLLAPSED) + trigger.setAttribute('aria-expanded', false) } } } @@ -243,19 +240,16 @@ class Collapse { const complete = () => { this.setTransitioning(false) - $(this._element) - .removeClass(ClassName.COLLAPSING) - .addClass(ClassName.COLLAPSE) - .trigger(Event.HIDDEN) + this._element.classList.remove(ClassName.COLLAPSING) + this._element.classList.add(ClassName.COLLAPSE) + EventHandler.trigger(this._element, Event.HIDDEN) } this._element.style[dimension] = '' const transitionDuration = Util.getTransitionDurationFromElement(this._element) - $(this._element) - .one(Util.TRANSITION_END, complete) - - Util.emulateTransitionEnd(transitionDuration) + EventHandler.one(this._element, Util.TRANSITION_END, complete) + Util.emulateTransitionEnd(this._element, transitionDuration) } setTransitioning(isTransitioning) { @@ -263,7 +257,7 @@ class Collapse { } dispose() { - $.removeData(this._element, DATA_KEY) + Data.removeData(this._element, DATA_KEY) this._config = null this._parent = null @@ -285,7 +279,7 @@ class Collapse { } _getDimension() { - const hasWidth = $(this._element).hasClass(Dimension.WIDTH) + const hasWidth = this._element.classList.contains(Dimension.WIDTH) return hasWidth ? Dimension.WIDTH : Dimension.HEIGHT } @@ -300,14 +294,14 @@ class Collapse { parent = this._config.parent[0] } } else { - parent = document.querySelector(this._config.parent) + parent = SelectorEngine.findOne(this._config.parent) } const selector = `[data-toggle="collapse"][data-parent="${this._config.parent}"]` - const children = [].slice.call(parent.querySelectorAll(selector)) - $(children).each((i, element) => { + const elements = Util.makeArray(SelectorEngine.find(selector, parent)) + elements.forEach((element) => { this._addAriaAndCollapsedClass( Collapse._getTargetFromElement(element), [element] @@ -318,12 +312,19 @@ class Collapse { } _addAriaAndCollapsedClass(element, triggerArray) { - const isOpen = $(element).hasClass(ClassName.SHOW) - - if (triggerArray.length) { - $(triggerArray) - .toggleClass(ClassName.COLLAPSED, !isOpen) - .attr('aria-expanded', isOpen) + if (element) { + const isOpen = element.classList.contains(ClassName.SHOW) + + if (triggerArray.length) { + triggerArray.forEach((elem) => { + if (!isOpen) { + elem.classList.add(ClassName.COLLAPSED) + } else { + elem.classList.remove(ClassName.COLLAPSED) + } + elem.setAttribute('aria-expanded', isOpen) + }) + } } } @@ -334,31 +335,34 @@ class Collapse { return selector ? document.querySelector(selector) : null } - static _jQueryInterface(config) { - return this.each(function () { - const $this = $(this) - let data = $this.data(DATA_KEY) - const _config = { - ...Default, - ...$this.data(), - ...typeof config === 'object' && config ? config : {} - } + static _collapseInterface(element, config) { + let data = Data.getData(element, DATA_KEY) + const _config = { + ...Default, + ...Util.getDataAttributes(element), + ...typeof config === 'object' && config ? config : {} + } - if (!data && _config.toggle && /show|hide/.test(config)) { - _config.toggle = false - } + if (!data && _config.toggle && /show|hide/.test(config)) { + _config.toggle = false + } - if (!data) { - data = new Collapse(this, _config) - $this.data(DATA_KEY, data) - } + if (!data) { + data = new Collapse(element, _config) + Data.setData(element, DATA_KEY, data) + } - if (typeof config === 'string') { - if (typeof data[config] === 'undefined') { - throw new TypeError(`No method named "${config}"`) - } - data[config]() + if (typeof config === 'string') { + if (typeof data[config] === 'undefined') { + throw new Error(`No method named "${config}"`) } + data[config]() + } + } + + static _jQueryInterface(config) { + return this.each(function () { + Collapse._collapseInterface(this, config) }) } } @@ -369,21 +373,31 @@ class Collapse { * ------------------------------------------------------------------------ */ -$(document).on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) { +EventHandler.on(document, Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) { // preventDefault only for <a> elements (which change the URL) not inside the collapsible element - if (event.currentTarget.tagName === 'A') { + if (event.target.tagName === 'A') { event.preventDefault() } - const $trigger = $(this) - const selector = Util.getSelectorFromElement(this) - const selectors = [].slice.call(document.querySelectorAll(selector)) + const triggerData = Util.getDataAttributes(this) + const selector = Util.getSelectorFromElement(this) + const selectorElements = Util.makeArray(SelectorEngine.find(selector)) + + selectorElements.forEach((element) => { + const data = Data.getData(element, DATA_KEY) + let config + if (data) { + // update parent attribute + if (data._parent === null && typeof triggerData.parent === 'string') { + data._config.parent = triggerData.parent + data._parent = data._getParent() + } + config = 'toggle' + } else { + config = triggerData + } - $(selectors).each(function () { - const $target = $(this) - const data = $target.data(DATA_KEY) - const config = data ? 'toggle' : $trigger.data() - Collapse._jQueryInterface.call($target, config) + Collapse._collapseInterface(element, config) }) }) @@ -391,13 +405,18 @@ $(document).on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) { * ------------------------------------------------------------------------ * jQuery * ------------------------------------------------------------------------ + * add .collapse to jQuery only if jQuery is present */ -$.fn[NAME] = Collapse._jQueryInterface -$.fn[NAME].Constructor = Collapse -$.fn[NAME].noConflict = () => { - $.fn[NAME] = JQUERY_NO_CONFLICT - return Collapse._jQueryInterface +const $ = Util.jQuery +if (typeof $ !== 'undefined') { + const JQUERY_NO_CONFLICT = $.fn[NAME] + $.fn[NAME] = Collapse._jQueryInterface + $.fn[NAME].Constructor = Collapse + $.fn[NAME].noConflict = () => { + $.fn[NAME] = JQUERY_NO_CONFLICT + return Collapse._jQueryInterface + } } export default Collapse |