From c21506d499c682ea5e31107ce264b224c4bb644d Mon Sep 17 00:00:00 2001 From: Sascha Date: Sun, 1 Nov 2020 14:32:36 +0100 Subject: Fix TypeError when Bootstrap is included in `head` (#32024) * extend jquery after domContentLoaded event is fired * add unittest for util onDOMContentLoaded * wait for trigger jquery event after domContentLoaded * remove domcontentready from eventHandler * move istanbul ignore statements to correct line Co-authored-by: XhmikosR --- js/src/alert.js | 24 +++++++++++++----------- js/src/button.js | 28 +++++++++++++++------------- js/src/carousel.js | 25 ++++++++++++++----------- js/src/collapse.js | 25 ++++++++++++++----------- js/src/dom/event-handler.js | 2 +- js/src/dropdown.js | 25 ++++++++++++++----------- js/src/modal.js | 25 ++++++++++++++----------- js/src/popover.js | 26 ++++++++++++++------------ js/src/scrollspy.js | 25 ++++++++++++++----------- js/src/tab.js | 25 ++++++++++++++----------- js/src/toast.js | 25 ++++++++++++++----------- js/src/tooltip.js | 25 ++++++++++++++----------- js/src/util/index.js | 13 +++++++++++-- js/tests/unit/util/index.spec.js | 19 +++++++++++++++++++ 14 files changed, 185 insertions(+), 127 deletions(-) (limited to 'js') diff --git a/js/src/alert.js b/js/src/alert.js index b337fbc577..f3b4245afa 100644 --- a/js/src/alert.js +++ b/js/src/alert.js @@ -7,6 +7,7 @@ import { getjQuery, + onDOMContentLoaded, TRANSITION_END, emulateTransitionEnd, getElementFromSelector, @@ -146,8 +147,6 @@ class Alert { */ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DISMISS, Alert.handleDismiss(new Alert())) -const $ = getjQuery() - /** * ------------------------------------------------------------------------ * jQuery @@ -155,15 +154,18 @@ const $ = getjQuery() * add .alert to jQuery only if jQuery is present */ -/* istanbul ignore if */ -if ($) { - const JQUERY_NO_CONFLICT = $.fn[NAME] - $.fn[NAME] = Alert.jQueryInterface - $.fn[NAME].Constructor = Alert - $.fn[NAME].noConflict = () => { - $.fn[NAME] = JQUERY_NO_CONFLICT - return Alert.jQueryInterface +onDOMContentLoaded(() => { + const $ = getjQuery() + /* istanbul ignore if */ + if ($) { + const JQUERY_NO_CONFLICT = $.fn[NAME] + $.fn[NAME] = Alert.jQueryInterface + $.fn[NAME].Constructor = Alert + $.fn[NAME].noConflict = () => { + $.fn[NAME] = JQUERY_NO_CONFLICT + return Alert.jQueryInterface + } } -} +}) export default Alert diff --git a/js/src/button.js b/js/src/button.js index f80b5e28cf..c520aa117d 100644 --- a/js/src/button.js +++ b/js/src/button.js @@ -5,7 +5,7 @@ * -------------------------------------------------------------------------- */ -import { getjQuery } from './util/index' +import { getjQuery, onDOMContentLoaded } from './util/index' import Data from './dom/data' import EventHandler from './dom/event-handler' @@ -97,24 +97,26 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, event => { data.toggle() }) -const $ = getjQuery() - /** * ------------------------------------------------------------------------ * jQuery * ------------------------------------------------------------------------ * add .button to jQuery only if jQuery is present */ -/* istanbul ignore if */ -if ($) { - const JQUERY_NO_CONFLICT = $.fn[NAME] - $.fn[NAME] = Button.jQueryInterface - $.fn[NAME].Constructor = Button - - $.fn[NAME].noConflict = () => { - $.fn[NAME] = JQUERY_NO_CONFLICT - return Button.jQueryInterface + +onDOMContentLoaded(() => { + const $ = getjQuery() + /* istanbul ignore if */ + if ($) { + const JQUERY_NO_CONFLICT = $.fn[NAME] + $.fn[NAME] = Button.jQueryInterface + $.fn[NAME].Constructor = Button + + $.fn[NAME].noConflict = () => { + $.fn[NAME] = JQUERY_NO_CONFLICT + return Button.jQueryInterface + } } -} +}) export default Button diff --git a/js/src/carousel.js b/js/src/carousel.js index ed8e2b89a6..b9bb504361 100644 --- a/js/src/carousel.js +++ b/js/src/carousel.js @@ -7,6 +7,7 @@ import { getjQuery, + onDOMContentLoaded, TRANSITION_END, emulateTransitionEnd, getElementFromSelector, @@ -611,23 +612,25 @@ EventHandler.on(window, EVENT_LOAD_DATA_API, () => { } }) -const $ = getjQuery() - /** * ------------------------------------------------------------------------ * jQuery * ------------------------------------------------------------------------ * add .carousel to jQuery only if jQuery is present */ -/* istanbul ignore if */ -if ($) { - const JQUERY_NO_CONFLICT = $.fn[NAME] - $.fn[NAME] = Carousel.jQueryInterface - $.fn[NAME].Constructor = Carousel - $.fn[NAME].noConflict = () => { - $.fn[NAME] = JQUERY_NO_CONFLICT - return Carousel.jQueryInterface + +onDOMContentLoaded(() => { + const $ = getjQuery() + /* istanbul ignore if */ + if ($) { + const JQUERY_NO_CONFLICT = $.fn[NAME] + $.fn[NAME] = Carousel.jQueryInterface + $.fn[NAME].Constructor = Carousel + $.fn[NAME].noConflict = () => { + $.fn[NAME] = JQUERY_NO_CONFLICT + return Carousel.jQueryInterface + } } -} +}) export default Carousel diff --git a/js/src/collapse.js b/js/src/collapse.js index f90238bc51..7788d0025a 100644 --- a/js/src/collapse.js +++ b/js/src/collapse.js @@ -7,6 +7,7 @@ import { getjQuery, + onDOMContentLoaded, TRANSITION_END, emulateTransitionEnd, getSelectorFromElement, @@ -408,23 +409,25 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function ( }) }) -const $ = getjQuery() - /** * ------------------------------------------------------------------------ * jQuery * ------------------------------------------------------------------------ * add .collapse to jQuery only if jQuery is present */ -/* istanbul ignore if */ -if ($) { - 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 + +onDOMContentLoaded(() => { + const $ = getjQuery() + /* istanbul ignore if */ + if ($) { + 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 diff --git a/js/src/dom/event-handler.js b/js/src/dom/event-handler.js index dced8d9dee..1d109c57ec 100644 --- a/js/src/dom/event-handler.js +++ b/js/src/dom/event-handler.js @@ -14,7 +14,6 @@ import { defaultPreventedPreservedOnDispatch } from './polyfill' * ------------------------------------------------------------------------ */ -const $ = getjQuery() const namespaceRegex = /[^.]*(?=\..*)\.|.*/ const stripNameRegex = /\..*/ const stripUidRegex = /::\d+$/ @@ -272,6 +271,7 @@ const EventHandler = { return null } + const $ = getjQuery() const typeEvent = event.replace(stripNameRegex, '') const inNamespace = event !== typeEvent const isNative = nativeEvents.indexOf(typeEvent) > -1 diff --git a/js/src/dropdown.js b/js/src/dropdown.js index 28d8b72992..46311ee8ad 100644 --- a/js/src/dropdown.js +++ b/js/src/dropdown.js @@ -7,6 +7,7 @@ import { getjQuery, + onDOMContentLoaded, getElementFromSelector, isElement, isVisible, @@ -512,23 +513,25 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function ( }) EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_FORM_CHILD, e => e.stopPropagation()) -const $ = getjQuery() - /** * ------------------------------------------------------------------------ * jQuery * ------------------------------------------------------------------------ * add .dropdown to jQuery only if jQuery is present */ -/* istanbul ignore if */ -if ($) { - const JQUERY_NO_CONFLICT = $.fn[NAME] - $.fn[NAME] = Dropdown.jQueryInterface - $.fn[NAME].Constructor = Dropdown - $.fn[NAME].noConflict = () => { - $.fn[NAME] = JQUERY_NO_CONFLICT - return Dropdown.jQueryInterface + +onDOMContentLoaded(() => { + const $ = getjQuery() + /* istanbul ignore if */ + if ($) { + const JQUERY_NO_CONFLICT = $.fn[NAME] + $.fn[NAME] = Dropdown.jQueryInterface + $.fn[NAME].Constructor = Dropdown + $.fn[NAME].noConflict = () => { + $.fn[NAME] = JQUERY_NO_CONFLICT + return Dropdown.jQueryInterface + } } -} +}) export default Dropdown diff --git a/js/src/modal.js b/js/src/modal.js index 4c1db3d5ca..adddb62e28 100644 --- a/js/src/modal.js +++ b/js/src/modal.js @@ -7,6 +7,7 @@ import { getjQuery, + onDOMContentLoaded, TRANSITION_END, emulateTransitionEnd, getElementFromSelector, @@ -607,23 +608,25 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function ( data.show(this) }) -const $ = getjQuery() - /** * ------------------------------------------------------------------------ * jQuery * ------------------------------------------------------------------------ * add .modal to jQuery only if jQuery is present */ -/* istanbul ignore if */ -if ($) { - const JQUERY_NO_CONFLICT = $.fn[NAME] - $.fn[NAME] = Modal.jQueryInterface - $.fn[NAME].Constructor = Modal - $.fn[NAME].noConflict = () => { - $.fn[NAME] = JQUERY_NO_CONFLICT - return Modal.jQueryInterface + +onDOMContentLoaded(() => { + const $ = getjQuery() + /* istanbul ignore if */ + if ($) { + const JQUERY_NO_CONFLICT = $.fn[NAME] + $.fn[NAME] = Modal.jQueryInterface + $.fn[NAME].Constructor = Modal + $.fn[NAME].noConflict = () => { + $.fn[NAME] = JQUERY_NO_CONFLICT + return Modal.jQueryInterface + } } -} +}) export default Modal diff --git a/js/src/popover.js b/js/src/popover.js index 54abe1f961..a4980a19e9 100644 --- a/js/src/popover.js +++ b/js/src/popover.js @@ -5,7 +5,7 @@ * -------------------------------------------------------------------------- */ -import { getjQuery } from './util/index' +import { getjQuery, onDOMContentLoaded } from './util/index' import Data from './dom/data' import SelectorEngine from './dom/selector-engine' import Tooltip from './tooltip' @@ -167,22 +167,24 @@ class Popover extends Tooltip { } } -const $ = getjQuery() - /** * ------------------------------------------------------------------------ * jQuery * ------------------------------------------------------------------------ */ -/* istanbul ignore if */ -if ($) { - const JQUERY_NO_CONFLICT = $.fn[NAME] - $.fn[NAME] = Popover.jQueryInterface - $.fn[NAME].Constructor = Popover - $.fn[NAME].noConflict = () => { - $.fn[NAME] = JQUERY_NO_CONFLICT - return Popover.jQueryInterface + +onDOMContentLoaded(() => { + const $ = getjQuery() + /* istanbul ignore if */ + if ($) { + const JQUERY_NO_CONFLICT = $.fn[NAME] + $.fn[NAME] = Popover.jQueryInterface + $.fn[NAME].Constructor = Popover + $.fn[NAME].noConflict = () => { + $.fn[NAME] = JQUERY_NO_CONFLICT + return Popover.jQueryInterface + } } -} +}) export default Popover diff --git a/js/src/scrollspy.js b/js/src/scrollspy.js index d37ca923dc..a0061b7e1e 100644 --- a/js/src/scrollspy.js +++ b/js/src/scrollspy.js @@ -7,6 +7,7 @@ import { getjQuery, + onDOMContentLoaded, getSelectorFromElement, getUID, isElement, @@ -317,22 +318,24 @@ EventHandler.on(window, EVENT_LOAD_DATA_API, () => { .forEach(spy => new ScrollSpy(spy, Manipulator.getDataAttributes(spy))) }) -const $ = getjQuery() - /** * ------------------------------------------------------------------------ * jQuery * ------------------------------------------------------------------------ */ -/* istanbul ignore if */ -if ($) { - const JQUERY_NO_CONFLICT = $.fn[NAME] - $.fn[NAME] = ScrollSpy.jQueryInterface - $.fn[NAME].Constructor = ScrollSpy - $.fn[NAME].noConflict = () => { - $.fn[NAME] = JQUERY_NO_CONFLICT - return ScrollSpy.jQueryInterface + +onDOMContentLoaded(() => { + const $ = getjQuery() + /* istanbul ignore if */ + if ($) { + const JQUERY_NO_CONFLICT = $.fn[NAME] + $.fn[NAME] = ScrollSpy.jQueryInterface + $.fn[NAME].Constructor = ScrollSpy + $.fn[NAME].noConflict = () => { + $.fn[NAME] = JQUERY_NO_CONFLICT + return ScrollSpy.jQueryInterface + } } -} +}) export default ScrollSpy diff --git a/js/src/tab.js b/js/src/tab.js index 76895b8ddb..af0dfc97aa 100644 --- a/js/src/tab.js +++ b/js/src/tab.js @@ -7,6 +7,7 @@ import { getjQuery, + onDOMContentLoaded, TRANSITION_END, emulateTransitionEnd, getElementFromSelector, @@ -235,23 +236,25 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function ( data.show() }) -const $ = getjQuery() - /** * ------------------------------------------------------------------------ * jQuery * ------------------------------------------------------------------------ * add .tab to jQuery only if jQuery is present */ -/* istanbul ignore if */ -if ($) { - const JQUERY_NO_CONFLICT = $.fn[NAME] - $.fn[NAME] = Tab.jQueryInterface - $.fn[NAME].Constructor = Tab - $.fn[NAME].noConflict = () => { - $.fn[NAME] = JQUERY_NO_CONFLICT - return Tab.jQueryInterface + +onDOMContentLoaded(() => { + const $ = getjQuery() + /* istanbul ignore if */ + if ($) { + const JQUERY_NO_CONFLICT = $.fn[NAME] + $.fn[NAME] = Tab.jQueryInterface + $.fn[NAME].Constructor = Tab + $.fn[NAME].noConflict = () => { + $.fn[NAME] = JQUERY_NO_CONFLICT + return Tab.jQueryInterface + } } -} +}) export default Tab diff --git a/js/src/toast.js b/js/src/toast.js index 91eaba53f5..308a5d879b 100644 --- a/js/src/toast.js +++ b/js/src/toast.js @@ -7,6 +7,7 @@ import { getjQuery, + onDOMContentLoaded, TRANSITION_END, emulateTransitionEnd, getTransitionDurationFromElement, @@ -213,23 +214,25 @@ class Toast { } } -const $ = getjQuery() - /** * ------------------------------------------------------------------------ * jQuery * ------------------------------------------------------------------------ * add .toast to jQuery only if jQuery is present */ -/* istanbul ignore if */ -if ($) { - const JQUERY_NO_CONFLICT = $.fn[NAME] - $.fn[NAME] = Toast.jQueryInterface - $.fn[NAME].Constructor = Toast - $.fn[NAME].noConflict = () => { - $.fn[NAME] = JQUERY_NO_CONFLICT - return Toast.jQueryInterface + +onDOMContentLoaded(() => { + const $ = getjQuery() + /* istanbul ignore if */ + if ($) { + const JQUERY_NO_CONFLICT = $.fn[NAME] + $.fn[NAME] = Toast.jQueryInterface + $.fn[NAME].Constructor = Toast + $.fn[NAME].noConflict = () => { + $.fn[NAME] = JQUERY_NO_CONFLICT + return Toast.jQueryInterface + } } -} +}) export default Toast diff --git a/js/src/tooltip.js b/js/src/tooltip.js index 1203142ed0..4d9d3c4a2d 100644 --- a/js/src/tooltip.js +++ b/js/src/tooltip.js @@ -7,6 +7,7 @@ import { getjQuery, + onDOMContentLoaded, TRANSITION_END, emulateTransitionEnd, findShadowRoot, @@ -793,23 +794,25 @@ class Tooltip { } } -const $ = getjQuery() - /** * ------------------------------------------------------------------------ * jQuery * ------------------------------------------------------------------------ * add .tooltip to jQuery only if jQuery is present */ -/* istanbul ignore if */ -if ($) { - const JQUERY_NO_CONFLICT = $.fn[NAME] - $.fn[NAME] = Tooltip.jQueryInterface - $.fn[NAME].Constructor = Tooltip - $.fn[NAME].noConflict = () => { - $.fn[NAME] = JQUERY_NO_CONFLICT - return Tooltip.jQueryInterface + +onDOMContentLoaded(() => { + const $ = getjQuery() + /* istanbul ignore if */ + if ($) { + const JQUERY_NO_CONFLICT = $.fn[NAME] + $.fn[NAME] = Tooltip.jQueryInterface + $.fn[NAME].Constructor = Tooltip + $.fn[NAME].noConflict = () => { + $.fn[NAME] = JQUERY_NO_CONFLICT + return Tooltip.jQueryInterface + } } -} +}) export default Tooltip diff --git a/js/src/util/index.js b/js/src/util/index.js index 457b2e0273..0fd78c848d 100644 --- a/js/src/util/index.js +++ b/js/src/util/index.js @@ -180,8 +180,15 @@ const getjQuery = () => { return null } +const onDOMContentLoaded = callback => { + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', callback) + } else { + callback() + } +} + export { - getjQuery, TRANSITION_END, getUID, getSelectorFromElement, @@ -194,5 +201,7 @@ export { isVisible, findShadowRoot, noop, - reflow + reflow, + getjQuery, + onDOMContentLoaded } diff --git a/js/tests/unit/util/index.spec.js b/js/tests/unit/util/index.spec.js index 541c10baa7..f7cc379777 100644 --- a/js/tests/unit/util/index.spec.js +++ b/js/tests/unit/util/index.spec.js @@ -394,4 +394,23 @@ describe('Util', () => { expect(Util.getjQuery()).toEqual(null) }) }) + + describe('onDOMContentLoaded', () => { + it('should execute callback when DOMContentLoaded is fired', () => { + const spy = jasmine.createSpy() + spyOnProperty(document, 'readyState').and.returnValue('loading') + Util.onDOMContentLoaded(spy) + window.document.dispatchEvent(new Event('DOMContentLoaded', { + bubbles: true, + cancelable: true + })) + expect(spy).toHaveBeenCalled() + }) + + it('should execute callback if readyState is not "loading"', () => { + const spy = jasmine.createSpy() + Util.onDOMContentLoaded(spy) + expect(spy).toHaveBeenCalled() + }) + }) }) -- cgit v1.2.3