mirror of
https://github.com/twbs/bootstrap.git
synced 2025-02-26 23:54:23 +01:00
Be SSR friendly when accessing DOM objects
This commit is contained in:
parent
c44d99f55c
commit
8306213650
@ -1,8 +1,10 @@
|
||||
{
|
||||
"root": true,
|
||||
"plugins": ["ssr-friendly"],
|
||||
"extends": [
|
||||
"plugin:import/errors",
|
||||
"plugin:import/warnings",
|
||||
"plugin:ssr-friendly/recommended",
|
||||
"plugin:unicorn/recommended",
|
||||
"xo",
|
||||
"xo/browser"
|
||||
@ -50,6 +52,8 @@
|
||||
"error",
|
||||
"never"
|
||||
],
|
||||
"ssr-friendly/no-dom-globals-in-react-cc-render": "off",
|
||||
"ssr-friendly/no-dom-globals-in-react-fc": "off",
|
||||
"unicorn/explicit-length-check": "off",
|
||||
"unicorn/no-array-callback-reference": "off",
|
||||
"unicorn/no-array-method-this-argument": "off",
|
||||
|
@ -6,7 +6,12 @@
|
||||
*/
|
||||
|
||||
import Data from './dom/data'
|
||||
import { executeAfterTransition, getElement } from './util/index'
|
||||
import {
|
||||
executeAfterTransition,
|
||||
getElement,
|
||||
getWindow,
|
||||
getDocument
|
||||
} from './util/index'
|
||||
import EventHandler from './dom/event-handler'
|
||||
import Config from './util/config'
|
||||
|
||||
@ -30,6 +35,8 @@ class BaseComponent extends Config {
|
||||
}
|
||||
|
||||
this._element = element
|
||||
this._window = getWindow()
|
||||
this._document = getDocument()
|
||||
this._config = this._getConfig(config)
|
||||
|
||||
Data.set(this._element, this.constructor.DATA_KEY, this)
|
||||
|
@ -5,7 +5,7 @@
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
import { defineJQueryPlugin } from './util/index'
|
||||
import { defineJQueryPlugin, getDocument } from './util/index'
|
||||
import EventHandler from './dom/event-handler'
|
||||
import BaseComponent from './base-component'
|
||||
|
||||
@ -54,7 +54,7 @@ class Button extends BaseComponent {
|
||||
* Data API implementation
|
||||
*/
|
||||
|
||||
EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, event => {
|
||||
EventHandler.on(getDocument(), EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, event => {
|
||||
event.preventDefault()
|
||||
|
||||
const button = event.target.closest(SELECTOR_DATA_TOGGLE)
|
||||
|
@ -12,7 +12,9 @@ import {
|
||||
isRTL,
|
||||
isVisible,
|
||||
reflow,
|
||||
triggerTransitionEnd
|
||||
triggerTransitionEnd,
|
||||
getDocument,
|
||||
getWindow
|
||||
} from './util/index'
|
||||
import EventHandler from './dom/event-handler'
|
||||
import Manipulator from './dom/manipulator'
|
||||
@ -129,7 +131,7 @@ class Carousel extends BaseComponent {
|
||||
// FIXME TODO use `document.visibilityState`
|
||||
// Don't call next when the page isn't visible
|
||||
// or the carousel or its parent isn't visible
|
||||
if (!document.hidden && isVisible(this._element)) {
|
||||
if (!this._document.hidden && isVisible(this._element)) {
|
||||
this.next()
|
||||
}
|
||||
}
|
||||
@ -505,9 +507,9 @@ class Carousel extends BaseComponent {
|
||||
* Data API implementation
|
||||
*/
|
||||
|
||||
EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_SLIDE, Carousel.dataApiClickHandler)
|
||||
EventHandler.on(getDocument(), EVENT_CLICK_DATA_API, SELECTOR_DATA_SLIDE, Carousel.dataApiClickHandler)
|
||||
|
||||
EventHandler.on(window, EVENT_LOAD_DATA_API, () => {
|
||||
EventHandler.on(getWindow(), EVENT_LOAD_DATA_API, () => {
|
||||
const carousels = SelectorEngine.find(SELECTOR_DATA_RIDE)
|
||||
|
||||
for (const carousel of carousels) {
|
||||
|
@ -10,7 +10,8 @@ import {
|
||||
getElement,
|
||||
getElementFromSelector,
|
||||
getSelectorFromElement,
|
||||
reflow
|
||||
reflow,
|
||||
getDocument
|
||||
} from './util/index'
|
||||
import EventHandler from './dom/event-handler'
|
||||
import SelectorEngine from './dom/selector-engine'
|
||||
@ -279,7 +280,7 @@ class Collapse extends BaseComponent {
|
||||
* Data API implementation
|
||||
*/
|
||||
|
||||
EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
|
||||
EventHandler.on(getDocument(), 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.target.tagName === 'A' || (event.delegateTarget && event.delegateTarget.tagName === 'A')) {
|
||||
event.preventDefault()
|
||||
|
@ -5,6 +5,8 @@
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
import { getWindow } from '../util/index'
|
||||
|
||||
function normalizeData(value) {
|
||||
if (value === 'true') {
|
||||
return true
|
||||
@ -61,10 +63,11 @@ const Manipulator = {
|
||||
|
||||
offset(element) {
|
||||
const rect = element.getBoundingClientRect()
|
||||
const windowRef = getWindow()
|
||||
|
||||
return {
|
||||
top: rect.top + window.pageYOffset,
|
||||
left: rect.left + window.pageXOffset
|
||||
top: rect.top + windowRef.pageYOffset,
|
||||
left: rect.left + windowRef.pageXOffset
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -5,18 +5,18 @@
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
import { isDisabled, isVisible } from '../util/index'
|
||||
import { getDocument, isDisabled, isVisible } from '../util/index'
|
||||
|
||||
/**
|
||||
* Constants
|
||||
*/
|
||||
|
||||
const SelectorEngine = {
|
||||
find(selector, element = document.documentElement) {
|
||||
find(selector, element = getDocument().documentElement) {
|
||||
return [].concat(...Element.prototype.querySelectorAll.call(element, selector))
|
||||
},
|
||||
|
||||
findOne(selector, element = document.documentElement) {
|
||||
findOne(selector, element = getDocument().documentElement) {
|
||||
return Element.prototype.querySelector.call(element, selector)
|
||||
},
|
||||
|
||||
|
@ -14,7 +14,8 @@ import {
|
||||
isElement,
|
||||
isRTL,
|
||||
isVisible,
|
||||
noop
|
||||
noop,
|
||||
getDocument
|
||||
} from './util/index'
|
||||
import EventHandler from './dom/event-handler'
|
||||
import Manipulator from './dom/manipulator'
|
||||
@ -133,8 +134,8 @@ class Dropdown extends BaseComponent {
|
||||
// empty mouseover listeners to the body's immediate children;
|
||||
// only needed because of broken event delegation on iOS
|
||||
// https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html
|
||||
if ('ontouchstart' in document.documentElement && !this._parent.closest(SELECTOR_NAVBAR_NAV)) {
|
||||
for (const element of [].concat(...document.body.children)) {
|
||||
if ('ontouchstart' in this._document.documentElement && !this._parent.closest(SELECTOR_NAVBAR_NAV)) {
|
||||
for (const element of [].concat(...this._document.body.children)) {
|
||||
EventHandler.on(element, 'mouseover', noop)
|
||||
}
|
||||
}
|
||||
@ -434,11 +435,13 @@ class Dropdown extends BaseComponent {
|
||||
* Data API implementation
|
||||
*/
|
||||
|
||||
EventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_DATA_TOGGLE, Dropdown.dataApiKeydownHandler)
|
||||
EventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_MENU, Dropdown.dataApiKeydownHandler)
|
||||
EventHandler.on(document, EVENT_CLICK_DATA_API, Dropdown.clearMenus)
|
||||
EventHandler.on(document, EVENT_KEYUP_DATA_API, Dropdown.clearMenus)
|
||||
EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
|
||||
const documentRef = getDocument()
|
||||
|
||||
EventHandler.on(documentRef, EVENT_KEYDOWN_DATA_API, SELECTOR_DATA_TOGGLE, Dropdown.dataApiKeydownHandler)
|
||||
EventHandler.on(documentRef, EVENT_KEYDOWN_DATA_API, SELECTOR_MENU, Dropdown.dataApiKeydownHandler)
|
||||
EventHandler.on(documentRef, EVENT_CLICK_DATA_API, Dropdown.clearMenus)
|
||||
EventHandler.on(documentRef, EVENT_KEYUP_DATA_API, Dropdown.clearMenus)
|
||||
EventHandler.on(documentRef, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
|
||||
event.preventDefault()
|
||||
Dropdown.getOrCreateInstance(this).toggle()
|
||||
})
|
||||
|
@ -5,7 +5,14 @@
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
import { defineJQueryPlugin, getElementFromSelector, isRTL, isVisible, reflow } from './util/index'
|
||||
import {
|
||||
defineJQueryPlugin,
|
||||
getElementFromSelector,
|
||||
isRTL,
|
||||
isVisible,
|
||||
reflow,
|
||||
getDocument
|
||||
} from './util/index'
|
||||
import EventHandler from './dom/event-handler'
|
||||
import SelectorEngine from './dom/selector-engine'
|
||||
import ScrollBarHelper from './util/scrollbar'
|
||||
@ -184,8 +191,8 @@ class Modal extends BaseComponent {
|
||||
|
||||
_showElement(relatedTarget) {
|
||||
// try to append dynamic modal
|
||||
if (!document.body.contains(this._element)) {
|
||||
document.body.append(this._element)
|
||||
if (!this._document.body.contains(this._element)) {
|
||||
this._document.body.append(this._element)
|
||||
}
|
||||
|
||||
this._element.style.display = 'block'
|
||||
@ -255,7 +262,7 @@ class Modal extends BaseComponent {
|
||||
this._isTransitioning = false
|
||||
|
||||
this._backdrop.hide(() => {
|
||||
document.body.classList.remove(CLASS_NAME_OPEN)
|
||||
this._document.body.classList.remove(CLASS_NAME_OPEN)
|
||||
this._resetAdjustments()
|
||||
this._scrollBar.reset()
|
||||
EventHandler.trigger(this._element, EVENT_HIDDEN)
|
||||
@ -272,7 +279,7 @@ class Modal extends BaseComponent {
|
||||
return
|
||||
}
|
||||
|
||||
const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight
|
||||
const isModalOverflowing = this._element.scrollHeight > this._document.documentElement.clientHeight
|
||||
const initialOverflowY = this._element.style.overflowY
|
||||
// return if the following background transition hasn't yet completed
|
||||
if (initialOverflowY === 'hidden' || this._element.classList.contains(CLASS_NAME_STATIC)) {
|
||||
@ -299,7 +306,7 @@ class Modal extends BaseComponent {
|
||||
*/
|
||||
|
||||
_adjustDialog() {
|
||||
const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight
|
||||
const isModalOverflowing = this._element.scrollHeight > this._document.documentElement.clientHeight
|
||||
const scrollbarWidth = this._scrollBar.getWidth()
|
||||
const isBodyOverflowing = scrollbarWidth > 0
|
||||
|
||||
@ -341,7 +348,7 @@ class Modal extends BaseComponent {
|
||||
* Data API implementation
|
||||
*/
|
||||
|
||||
EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
|
||||
EventHandler.on(getDocument(), EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
|
||||
const target = getElementFromSelector(this)
|
||||
|
||||
if (['A', 'AREA'].includes(this.tagName)) {
|
||||
|
@ -9,7 +9,9 @@ import {
|
||||
defineJQueryPlugin,
|
||||
getElementFromSelector,
|
||||
isDisabled,
|
||||
isVisible
|
||||
isVisible,
|
||||
getDocument,
|
||||
getWindow
|
||||
} from './util/index'
|
||||
import ScrollBarHelper from './util/scrollbar'
|
||||
import EventHandler from './dom/event-handler'
|
||||
@ -209,7 +211,7 @@ class Offcanvas extends BaseComponent {
|
||||
* Data API implementation
|
||||
*/
|
||||
|
||||
EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
|
||||
EventHandler.on(getDocument(), EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
|
||||
const target = getElementFromSelector(this)
|
||||
|
||||
if (['A', 'AREA'].includes(this.tagName)) {
|
||||
@ -237,7 +239,7 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (
|
||||
data.toggle(this)
|
||||
})
|
||||
|
||||
EventHandler.on(window, EVENT_LOAD_DATA_API, () => {
|
||||
EventHandler.on(getWindow(), EVENT_LOAD_DATA_API, () => {
|
||||
for (const selector of SelectorEngine.find(OPEN_SELECTOR)) {
|
||||
Offcanvas.getOrCreateInstance(selector).show()
|
||||
}
|
||||
|
@ -5,7 +5,12 @@
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
import { defineJQueryPlugin, getElement, getSelectorFromElement } from './util/index'
|
||||
import {
|
||||
defineJQueryPlugin,
|
||||
getElement,
|
||||
getSelectorFromElement,
|
||||
getWindow
|
||||
} from './util/index'
|
||||
import EventHandler from './dom/event-handler'
|
||||
import Manipulator from './dom/manipulator'
|
||||
import SelectorEngine from './dom/selector-engine'
|
||||
@ -58,7 +63,7 @@ const DefaultType = {
|
||||
class ScrollSpy extends BaseComponent {
|
||||
constructor(element, config) {
|
||||
super(element, config)
|
||||
this._scrollElement = this._element.tagName === 'BODY' ? window : this._element
|
||||
this._scrollElement = this._element.tagName === 'BODY' ? this._window : this._element
|
||||
this._offsets = []
|
||||
this._targets = []
|
||||
this._activeTarget = null
|
||||
@ -137,14 +142,14 @@ class ScrollSpy extends BaseComponent {
|
||||
|
||||
_getScrollHeight() {
|
||||
return this._scrollElement.scrollHeight || Math.max(
|
||||
document.body.scrollHeight,
|
||||
document.documentElement.scrollHeight
|
||||
this._document.body.scrollHeight,
|
||||
this._document.documentElement.scrollHeight
|
||||
)
|
||||
}
|
||||
|
||||
_getOffsetHeight() {
|
||||
return this._scrollElement === window ?
|
||||
window.innerHeight :
|
||||
this._window.innerHeight :
|
||||
this._scrollElement.getBoundingClientRect().height
|
||||
}
|
||||
|
||||
@ -251,7 +256,7 @@ class ScrollSpy extends BaseComponent {
|
||||
* Data API implementation
|
||||
*/
|
||||
|
||||
EventHandler.on(window, EVENT_LOAD_DATA_API, () => {
|
||||
EventHandler.on(getWindow(), EVENT_LOAD_DATA_API, () => {
|
||||
for (const spy of SelectorEngine.find(SELECTOR_DATA_SPY)) {
|
||||
new ScrollSpy(spy) // eslint-disable-line no-new
|
||||
}
|
||||
|
@ -5,7 +5,13 @@
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
import { defineJQueryPlugin, getElementFromSelector, isDisabled, reflow } from './util/index'
|
||||
import {
|
||||
defineJQueryPlugin,
|
||||
getDocument,
|
||||
getElementFromSelector,
|
||||
isDisabled,
|
||||
reflow
|
||||
} from './util/index'
|
||||
import EventHandler from './dom/event-handler'
|
||||
import SelectorEngine from './dom/selector-engine'
|
||||
import BaseComponent from './base-component'
|
||||
@ -177,7 +183,7 @@ class Tab extends BaseComponent {
|
||||
* Data API implementation
|
||||
*/
|
||||
|
||||
EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
|
||||
EventHandler.on(getDocument(), EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
|
||||
if (['A', 'AREA'].includes(this.tagName)) {
|
||||
event.preventDefault()
|
||||
}
|
||||
|
@ -245,8 +245,8 @@ class Tooltip extends BaseComponent {
|
||||
// empty mouseover listeners to the body's immediate children;
|
||||
// only needed because of broken event delegation on iOS
|
||||
// https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html
|
||||
if ('ontouchstart' in document.documentElement) {
|
||||
for (const element of [].concat(...document.body.children)) {
|
||||
if ('ontouchstart' in this._document.documentElement) {
|
||||
for (const element of [].concat(...this._document.body.children)) {
|
||||
EventHandler.on(element, 'mouseover', noop)
|
||||
}
|
||||
}
|
||||
@ -280,8 +280,8 @@ class Tooltip extends BaseComponent {
|
||||
|
||||
// If this is a touch-enabled device we remove the extra
|
||||
// empty mouseover listeners we added for iOS support
|
||||
if ('ontouchstart' in document.documentElement) {
|
||||
for (const element of [].concat(...document.body.children)) {
|
||||
if ('ontouchstart' in this._document.documentElement) {
|
||||
for (const element of [].concat(...this._document.body.children)) {
|
||||
EventHandler.off(element, 'mouseover', noop)
|
||||
}
|
||||
}
|
||||
|
@ -6,13 +6,13 @@
|
||||
*/
|
||||
|
||||
import EventHandler from '../dom/event-handler'
|
||||
import { getElementFromSelector, isDisabled } from './index'
|
||||
import { getElementFromSelector, isDisabled, getDocument } from './index'
|
||||
|
||||
const enableDismissTrigger = (component, method = 'hide') => {
|
||||
const clickEvent = `click.dismiss${component.EVENT_KEY}`
|
||||
const name = component.NAME
|
||||
|
||||
EventHandler.on(document, clickEvent, `[data-bs-dismiss="${name}"]`, function (event) {
|
||||
EventHandler.on(getDocument(), clickEvent, `[data-bs-dismiss="${name}"]`, function (event) {
|
||||
if (['A', 'AREA'].includes(this.tagName)) {
|
||||
event.preventDefault()
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
import EventHandler from '../dom/event-handler'
|
||||
import SelectorEngine from '../dom/selector-engine'
|
||||
import { getDocument } from './index'
|
||||
import Config from './config'
|
||||
|
||||
/**
|
||||
@ -43,6 +44,7 @@ class FocusTrap extends Config {
|
||||
this._config = this._getConfig(config)
|
||||
this._isActive = false
|
||||
this._lastTabNavDirection = null
|
||||
this._document = getDocument()
|
||||
}
|
||||
|
||||
// Getters
|
||||
@ -68,9 +70,9 @@ class FocusTrap extends Config {
|
||||
this._config.trapElement.focus()
|
||||
}
|
||||
|
||||
EventHandler.off(document, EVENT_KEY) // guard against infinite focus loop
|
||||
EventHandler.on(document, EVENT_FOCUSIN, event => this._handleFocusin(event))
|
||||
EventHandler.on(document, EVENT_KEYDOWN_TAB, event => this._handleKeydown(event))
|
||||
EventHandler.off(this._document, EVENT_KEY) // guard against infinite focus loop
|
||||
EventHandler.on(this._document, EVENT_FOCUSIN, event => this._handleFocusin(event))
|
||||
EventHandler.on(this._document, EVENT_KEYDOWN_TAB, event => this._handleKeydown(event))
|
||||
|
||||
this._isActive = true
|
||||
}
|
||||
@ -81,14 +83,14 @@ class FocusTrap extends Config {
|
||||
}
|
||||
|
||||
this._isActive = false
|
||||
EventHandler.off(document, EVENT_KEY)
|
||||
EventHandler.off(this._document, EVENT_KEY)
|
||||
}
|
||||
|
||||
// Private
|
||||
_handleFocusin(event) {
|
||||
const { trapElement } = this._config
|
||||
|
||||
if (event.target === document || event.target === trapElement || trapElement.contains(event.target)) {
|
||||
if (event.target === this._document || event.target === trapElement || trapElement.contains(event.target)) {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,7 @@ const toType = object => {
|
||||
const getUID = prefix => {
|
||||
do {
|
||||
prefix += Math.floor(Math.random() * MAX_UID)
|
||||
} while (document.getElementById(prefix))
|
||||
} while (getDocument().getElementById(prefix))
|
||||
|
||||
return prefix
|
||||
}
|
||||
@ -59,7 +59,7 @@ const getSelectorFromElement = element => {
|
||||
const selector = getSelector(element)
|
||||
|
||||
if (selector) {
|
||||
return document.querySelector(selector) ? selector : null
|
||||
return getDocument().querySelector(selector) ? selector : null
|
||||
}
|
||||
|
||||
return null
|
||||
@ -68,7 +68,7 @@ const getSelectorFromElement = element => {
|
||||
const getElementFromSelector = element => {
|
||||
const selector = getSelector(element)
|
||||
|
||||
return selector ? document.querySelector(selector) : null
|
||||
return selector ? getDocument().querySelector(selector) : null
|
||||
}
|
||||
|
||||
const getTransitionDurationFromElement = element => {
|
||||
@ -77,7 +77,7 @@ const getTransitionDurationFromElement = element => {
|
||||
}
|
||||
|
||||
// Get transition-duration of the element
|
||||
let { transitionDuration, transitionDelay } = window.getComputedStyle(element)
|
||||
let { transitionDuration, transitionDelay } = getWindow().getComputedStyle(element)
|
||||
|
||||
const floatTransitionDuration = Number.parseFloat(transitionDuration)
|
||||
const floatTransitionDelay = Number.parseFloat(transitionDelay)
|
||||
@ -167,7 +167,7 @@ const isDisabled = element => {
|
||||
}
|
||||
|
||||
const findShadowRoot = element => {
|
||||
if (!document.documentElement.attachShadow) {
|
||||
if (!getDocument().documentElement.attachShadow) {
|
||||
return null
|
||||
}
|
||||
|
||||
@ -200,11 +200,12 @@ const noop = () => {}
|
||||
* @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation
|
||||
*/
|
||||
const reflow = element => {
|
||||
element.offsetHeight // eslint-disable-line no-unused-expressions
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
element.offsetHeight
|
||||
}
|
||||
|
||||
const getjQuery = () => {
|
||||
if (window.jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {
|
||||
if (getWindow().jQuery && !getDocument().body.hasAttribute('data-bs-no-jquery')) {
|
||||
return window.jQuery
|
||||
}
|
||||
|
||||
@ -214,10 +215,11 @@ const getjQuery = () => {
|
||||
const DOMContentLoadedCallbacks = []
|
||||
|
||||
const onDOMContentLoaded = callback => {
|
||||
if (document.readyState === 'loading') {
|
||||
const documentRef = getDocument()
|
||||
if (documentRef.readyState === 'loading') {
|
||||
// add listener on the first call when the document is in loading state
|
||||
if (!DOMContentLoadedCallbacks.length) {
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
documentRef.addEventListener('DOMContentLoaded', () => {
|
||||
for (const callback of DOMContentLoadedCallbacks) {
|
||||
callback()
|
||||
}
|
||||
@ -230,7 +232,7 @@ const onDOMContentLoaded = callback => {
|
||||
}
|
||||
}
|
||||
|
||||
const isRTL = () => document.documentElement.dir === 'rtl'
|
||||
const isRTL = () => getDocument().documentElement.dir === 'rtl'
|
||||
|
||||
const defineJQueryPlugin = plugin => {
|
||||
onDOMContentLoaded(() => {
|
||||
@ -312,11 +314,26 @@ const getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed
|
||||
return list[Math.max(0, Math.min(index, listLength - 1))]
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {window|{}} The proper element
|
||||
*/
|
||||
const getWindow = () => {
|
||||
return typeof window !== 'undefined' ? window : {}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {document|{}} The proper element
|
||||
*/
|
||||
const getDocument = () => {
|
||||
return typeof document !== 'undefined' ? document : {}
|
||||
}
|
||||
|
||||
export {
|
||||
defineJQueryPlugin,
|
||||
execute,
|
||||
executeAfterTransition,
|
||||
findShadowRoot,
|
||||
getDocument,
|
||||
getElement,
|
||||
getElementFromSelector,
|
||||
getjQuery,
|
||||
@ -324,6 +341,7 @@ export {
|
||||
getSelectorFromElement,
|
||||
getTransitionDurationFromElement,
|
||||
getUID,
|
||||
getWindow,
|
||||
isDisabled,
|
||||
isElement,
|
||||
isRTL,
|
||||
|
@ -5,6 +5,8 @@
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
import { getWindow } from './index'
|
||||
|
||||
const uriAttributes = new Set([
|
||||
'background',
|
||||
'cite',
|
||||
@ -91,7 +93,8 @@ export function sanitizeHtml(unsafeHtml, allowList, sanitizeFunction) {
|
||||
return sanitizeFunction(unsafeHtml)
|
||||
}
|
||||
|
||||
const domParser = new window.DOMParser()
|
||||
const windowRef = getWindow()
|
||||
const domParser = new windowRef.DOMParser()
|
||||
const createdDocument = domParser.parseFromString(unsafeHtml, 'text/html')
|
||||
const elements = [].concat(...createdDocument.body.querySelectorAll('*'))
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
|
||||
import SelectorEngine from '../dom/selector-engine'
|
||||
import Manipulator from '../dom/manipulator'
|
||||
import { isElement } from './index'
|
||||
import { isElement, getDocument, getWindow } from './index'
|
||||
|
||||
/**
|
||||
* Constants
|
||||
@ -24,14 +24,15 @@ const PROPERTY_MARGIN = 'margin-right'
|
||||
|
||||
class ScrollBarHelper {
|
||||
constructor() {
|
||||
this._element = document.body
|
||||
this._element = getDocument().body
|
||||
this._window = getWindow()
|
||||
}
|
||||
|
||||
// Public
|
||||
getWidth() {
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth#usage_notes
|
||||
const documentWidth = document.documentElement.clientWidth
|
||||
return Math.abs(window.innerWidth - documentWidth)
|
||||
return Math.abs(this._window.innerWidth - documentWidth)
|
||||
}
|
||||
|
||||
hide() {
|
||||
@ -64,12 +65,12 @@ class ScrollBarHelper {
|
||||
_setElementAttributes(selector, styleProperty, callback) {
|
||||
const scrollbarWidth = this.getWidth()
|
||||
const manipulationCallBack = element => {
|
||||
if (element !== this._element && window.innerWidth > element.clientWidth + scrollbarWidth) {
|
||||
if (element !== this._element && this._window.innerWidth > element.clientWidth + scrollbarWidth) {
|
||||
return
|
||||
}
|
||||
|
||||
this._saveInitialAttribute(element, styleProperty)
|
||||
const calculatedValue = window.getComputedStyle(element).getPropertyValue(styleProperty)
|
||||
const calculatedValue = this._window.getComputedStyle(element).getPropertyValue(styleProperty)
|
||||
element.style.setProperty(styleProperty, `${callback(Number.parseFloat(calculatedValue))}px`)
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
|
||||
import Config from './config'
|
||||
import EventHandler from '../dom/event-handler'
|
||||
import { execute } from './index'
|
||||
import { execute, getDocument, getWindow } from './index'
|
||||
|
||||
/**
|
||||
* Constants
|
||||
@ -52,7 +52,7 @@ class Swipe extends Config {
|
||||
|
||||
this._config = this._getConfig(config)
|
||||
this._deltaX = 0
|
||||
this._supportPointerEvents = Boolean(window.PointerEvent)
|
||||
this._supportPointerEvents = Boolean(getWindow().PointerEvent)
|
||||
this._initEvents()
|
||||
}
|
||||
|
||||
@ -139,7 +139,7 @@ class Swipe extends Config {
|
||||
|
||||
// Static
|
||||
static isSupported() {
|
||||
return 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0
|
||||
return 'ontouchstart' in getDocument().documentElement || navigator.maxTouchPoints > 0
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
import Tooltip from '../../dist/tooltip'
|
||||
import '../../dist/carousel'
|
||||
|
||||
// eslint-disable-next-line ssr-friendly/no-dom-globals-in-module-scope
|
||||
window.addEventListener('load', () => {
|
||||
[].concat(...document.querySelectorAll('[data-bs-toggle="tooltip"]'))
|
||||
.map(tooltipNode => new Tooltip(tooltipNode))
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { Tooltip } from '../../../dist/js/bootstrap.esm.js'
|
||||
|
||||
// eslint-disable-next-line ssr-friendly/no-dom-globals-in-module-scope
|
||||
window.addEventListener('load', () => {
|
||||
[].concat(...document.querySelectorAll('[data-bs-toggle="tooltip"]'))
|
||||
.map(tooltipNode => new Tooltip(tooltipNode))
|
||||
|
66
package-lock.json
generated
66
package-lock.json
generated
@ -24,6 +24,7 @@
|
||||
"eslint": "^8.8.0",
|
||||
"eslint-config-xo": "^0.39.0",
|
||||
"eslint-plugin-import": "^2.25.4",
|
||||
"eslint-plugin-ssr-friendly": "^1.0.5",
|
||||
"eslint-plugin-unicorn": "^40.1.0",
|
||||
"find-unused-sass-variables": "^3.1.0",
|
||||
"globby": "^11.0.4",
|
||||
@ -4319,6 +4320,45 @@
|
||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/eslint-plugin-ssr-friendly": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-ssr-friendly/-/eslint-plugin-ssr-friendly-1.0.5.tgz",
|
||||
"integrity": "sha512-F1vKfzhOnrIXhcx91Y3r1x8vjJAoCex25PUgYErOe6q95T4KuCTz6+LgGQ4TTvhBdCfNqu1U0krAHe3UNuEOqg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"globals": "^13.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": ">=0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-ssr-friendly/node_modules/globals": {
|
||||
"version": "13.12.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz",
|
||||
"integrity": "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"type-fest": "^0.20.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-ssr-friendly/node_modules/type-fest": {
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
|
||||
"integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-unicorn": {
|
||||
"version": "40.1.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-40.1.0.tgz",
|
||||
@ -14030,6 +14070,32 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"eslint-plugin-ssr-friendly": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-ssr-friendly/-/eslint-plugin-ssr-friendly-1.0.5.tgz",
|
||||
"integrity": "sha512-F1vKfzhOnrIXhcx91Y3r1x8vjJAoCex25PUgYErOe6q95T4KuCTz6+LgGQ4TTvhBdCfNqu1U0krAHe3UNuEOqg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"globals": "^13.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"globals": {
|
||||
"version": "13.12.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz",
|
||||
"integrity": "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"type-fest": "^0.20.2"
|
||||
}
|
||||
},
|
||||
"type-fest": {
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
|
||||
"integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"eslint-plugin-unicorn": {
|
||||
"version": "40.1.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-40.1.0.tgz",
|
||||
|
@ -113,6 +113,7 @@
|
||||
"eslint": "^8.8.0",
|
||||
"eslint-config-xo": "^0.39.0",
|
||||
"eslint-plugin-import": "^2.25.4",
|
||||
"eslint-plugin-ssr-friendly": "^1.0.5",
|
||||
"eslint-plugin-unicorn": "^40.1.0",
|
||||
"find-unused-sass-variables": "^3.1.0",
|
||||
"globby": "^11.0.4",
|
||||
|
Loading…
x
Reference in New Issue
Block a user