0
0
mirror of https://github.com/twbs/bootstrap.git synced 2025-03-14 14:29:30 +01:00

Remove jQuery support in plugins

This commit is contained in:
Mark Otto 2025-02-21 21:06:51 -08:00
parent 7020a7eba4
commit 6b09f36aea
15 changed files with 7 additions and 385 deletions

View File

@ -8,7 +8,6 @@
import BaseComponent from './base-component.js' import BaseComponent from './base-component.js'
import EventHandler from './dom/event-handler.js' import EventHandler from './dom/event-handler.js'
import { enableDismissTrigger } from './util/component-functions.js' import { enableDismissTrigger } from './util/component-functions.js'
import { defineJQueryPlugin } from './util/index.js'
/** /**
* Constants * Constants
@ -53,23 +52,6 @@ class Alert extends BaseComponent {
EventHandler.trigger(this._element, EVENT_CLOSED) EventHandler.trigger(this._element, EVENT_CLOSED)
this.dispose() this.dispose()
} }
// Static
static jQueryInterface(config) {
return this.each(function () {
const data = Alert.getOrCreateInstance(this)
if (typeof config !== 'string') {
return
}
if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {
throw new TypeError(`No method named "${config}"`)
}
data[config](this)
})
}
} }
/** /**
@ -78,10 +60,4 @@ class Alert extends BaseComponent {
enableDismissTrigger(Alert, 'close') enableDismissTrigger(Alert, 'close')
/**
* jQuery
*/
defineJQueryPlugin(Alert)
export default Alert export default Alert

View File

@ -7,7 +7,6 @@
import BaseComponent from './base-component.js' import BaseComponent from './base-component.js'
import EventHandler from './dom/event-handler.js' import EventHandler from './dom/event-handler.js'
import { defineJQueryPlugin } from './util/index.js'
/** /**
* Constants * Constants
@ -37,17 +36,6 @@ class Button extends BaseComponent {
// Toggle class and sync the `aria-pressed` attribute with the return value of the `.toggle()` method // Toggle class and sync the `aria-pressed` attribute with the return value of the `.toggle()` method
this._element.setAttribute('aria-pressed', this._element.classList.toggle(CLASS_NAME_ACTIVE)) this._element.setAttribute('aria-pressed', this._element.classList.toggle(CLASS_NAME_ACTIVE))
} }
// Static
static jQueryInterface(config) {
return this.each(function () {
const data = Button.getOrCreateInstance(this)
if (config === 'toggle') {
data[config]()
}
})
}
} }
/** /**
@ -63,10 +51,4 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, event => {
data.toggle() data.toggle()
}) })
/**
* jQuery
*/
defineJQueryPlugin(Button)
export default Button export default Button

View File

@ -10,7 +10,6 @@ import EventHandler from './dom/event-handler.js'
import Manipulator from './dom/manipulator.js' import Manipulator from './dom/manipulator.js'
import SelectorEngine from './dom/selector-engine.js' import SelectorEngine from './dom/selector-engine.js'
import { import {
defineJQueryPlugin,
getNextActiveElement, getNextActiveElement,
isRTL, isRTL,
isVisible, isVisible,
@ -404,25 +403,6 @@ class Carousel extends BaseComponent {
return order === ORDER_PREV ? DIRECTION_RIGHT : DIRECTION_LEFT return order === ORDER_PREV ? DIRECTION_RIGHT : DIRECTION_LEFT
} }
// Static
static jQueryInterface(config) {
return this.each(function () {
const data = Carousel.getOrCreateInstance(this, config)
if (typeof config === 'number') {
data.to(config)
return
}
if (typeof config === 'string') {
if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {
throw new TypeError(`No method named "${config}"`)
}
data[config]()
}
})
}
} }
/** /**
@ -465,10 +445,4 @@ EventHandler.on(window, EVENT_LOAD_DATA_API, () => {
} }
}) })
/**
* jQuery
*/
defineJQueryPlugin(Carousel)
export default Carousel export default Carousel

View File

@ -9,7 +9,6 @@ import BaseComponent from './base-component.js'
import EventHandler from './dom/event-handler.js' import EventHandler from './dom/event-handler.js'
import SelectorEngine from './dom/selector-engine.js' import SelectorEngine from './dom/selector-engine.js'
import { import {
defineJQueryPlugin,
getElement, getElement,
reflow reflow
} from './util/index.js' } from './util/index.js'
@ -251,26 +250,6 @@ class Collapse extends BaseComponent {
element.setAttribute('aria-expanded', isOpen) element.setAttribute('aria-expanded', isOpen)
} }
} }
// Static
static jQueryInterface(config) {
const _config = {}
if (typeof config === 'string' && /show|hide/.test(config)) {
_config.toggle = false
}
return this.each(function () {
const data = Collapse.getOrCreateInstance(this, _config)
if (typeof config === 'string') {
if (typeof data[config] === 'undefined') {
throw new TypeError(`No method named "${config}"`)
}
data[config]()
}
})
}
} }
/** /**
@ -288,10 +267,4 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (
} }
}) })
/**
* jQuery
*/
defineJQueryPlugin(Collapse)
export default Collapse export default Collapse

View File

@ -5,8 +5,6 @@
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
*/ */
import { getjQuery } from '../util/index.js'
/** /**
* Constants * Constants
*/ */
@ -261,38 +259,8 @@ const EventHandler = {
return null return null
} }
const $ = getjQuery() const evt = hydrateObj(new Event(event, { bubbles: true, cancelable: true }), args)
const typeEvent = getTypeEvent(event) element.dispatchEvent(evt)
const inNamespace = event !== typeEvent
let jQueryEvent = null
let bubbles = true
let nativeDispatch = true
let defaultPrevented = false
if (inNamespace && $) {
jQueryEvent = $.Event(event, args)
$(element).trigger(jQueryEvent)
bubbles = !jQueryEvent.isPropagationStopped()
nativeDispatch = !jQueryEvent.isImmediatePropagationStopped()
defaultPrevented = jQueryEvent.isDefaultPrevented()
}
const evt = hydrateObj(new Event(event, { bubbles, cancelable: true }), args)
if (defaultPrevented) {
evt.preventDefault()
}
if (nativeDispatch) {
element.dispatchEvent(evt)
}
if (evt.defaultPrevented && jQueryEvent) {
jQueryEvent.preventDefault()
}
return evt return evt
} }
} }

View File

@ -11,7 +11,6 @@ import EventHandler from './dom/event-handler.js'
import Manipulator from './dom/manipulator.js' import Manipulator from './dom/manipulator.js'
import SelectorEngine from './dom/selector-engine.js' import SelectorEngine from './dom/selector-engine.js'
import { import {
defineJQueryPlugin,
execute, execute,
getElement, getElement,
getNextActiveElement, getNextActiveElement,
@ -336,23 +335,6 @@ class Dropdown extends BaseComponent {
getNextActiveElement(items, target, key === ARROW_DOWN_KEY, !items.includes(target)).focus() getNextActiveElement(items, target, key === ARROW_DOWN_KEY, !items.includes(target)).focus()
} }
// Static
static jQueryInterface(config) {
return this.each(function () {
const data = Dropdown.getOrCreateInstance(this, config)
if (typeof config !== 'string') {
return
}
if (typeof data[config] === 'undefined') {
throw new TypeError(`No method named "${config}"`)
}
data[config]()
})
}
static clearMenus(event) { static clearMenus(event) {
if (event.button === RIGHT_MOUSE_BUTTON || (event.type === 'keyup' && event.key !== TAB_KEY)) { if (event.button === RIGHT_MOUSE_BUTTON || (event.type === 'keyup' && event.key !== TAB_KEY)) {
return return
@ -446,10 +428,4 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (
Dropdown.getOrCreateInstance(this).toggle() Dropdown.getOrCreateInstance(this).toggle()
}) })
/**
* jQuery
*/
defineJQueryPlugin(Dropdown)
export default Dropdown export default Dropdown

View File

@ -12,7 +12,7 @@ import Backdrop from './util/backdrop.js'
import { enableDismissTrigger } from './util/component-functions.js' import { enableDismissTrigger } from './util/component-functions.js'
import FocusTrap from './util/focustrap.js' import FocusTrap from './util/focustrap.js'
import { import {
defineJQueryPlugin, isRTL, isVisible, reflow isRTL, isVisible, reflow
} from './util/index.js' } from './util/index.js'
import ScrollBarHelper from './util/scrollbar.js' import ScrollBarHelper from './util/scrollbar.js'
@ -313,23 +313,6 @@ class Modal extends BaseComponent {
this._element.style.paddingLeft = '' this._element.style.paddingLeft = ''
this._element.style.paddingRight = '' this._element.style.paddingRight = ''
} }
// Static
static jQueryInterface(config, relatedTarget) {
return this.each(function () {
const data = Modal.getOrCreateInstance(this, config)
if (typeof config !== 'string') {
return
}
if (typeof data[config] === 'undefined') {
throw new TypeError(`No method named "${config}"`)
}
data[config](relatedTarget)
})
}
} }
/** /**
@ -369,10 +352,4 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (
enableDismissTrigger(Modal) enableDismissTrigger(Modal)
/**
* jQuery
*/
defineJQueryPlugin(Modal)
export default Modal export default Modal

View File

@ -12,7 +12,6 @@ import Backdrop from './util/backdrop.js'
import { enableDismissTrigger } from './util/component-functions.js' import { enableDismissTrigger } from './util/component-functions.js'
import FocusTrap from './util/focustrap.js' import FocusTrap from './util/focustrap.js'
import { import {
defineJQueryPlugin,
isDisabled, isDisabled,
isVisible isVisible
} from './util/index.js' } from './util/index.js'
@ -207,22 +206,6 @@ class Offcanvas extends BaseComponent {
}) })
} }
// Static
static jQueryInterface(config) {
return this.each(function () {
const data = Offcanvas.getOrCreateInstance(this, config)
if (typeof config !== 'string') {
return
}
if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {
throw new TypeError(`No method named "${config}"`)
}
data[config](this)
})
}
} }
/** /**
@ -273,10 +256,4 @@ EventHandler.on(window, EVENT_RESIZE, () => {
enableDismissTrigger(Offcanvas) enableDismissTrigger(Offcanvas)
/**
* jQuery
*/
defineJQueryPlugin(Offcanvas)
export default Offcanvas export default Offcanvas

View File

@ -6,7 +6,6 @@
*/ */
import Tooltip from './tooltip.js' import Tooltip from './tooltip.js'
import { defineJQueryPlugin } from './util/index.js'
/** /**
* Constants * Constants
@ -69,29 +68,6 @@ class Popover extends Tooltip {
_getContent() { _getContent() {
return this._resolvePossibleFunction(this._config.content) return this._resolvePossibleFunction(this._config.content)
} }
// Static
static jQueryInterface(config) {
return this.each(function () {
const data = Popover.getOrCreateInstance(this, config)
if (typeof config !== 'string') {
return
}
if (typeof data[config] === 'undefined') {
throw new TypeError(`No method named "${config}"`)
}
data[config]()
})
}
} }
/**
* jQuery
*/
defineJQueryPlugin(Popover)
export default Popover export default Popover

View File

@ -9,7 +9,7 @@ import BaseComponent from './base-component.js'
import EventHandler from './dom/event-handler.js' import EventHandler from './dom/event-handler.js'
import SelectorEngine from './dom/selector-engine.js' import SelectorEngine from './dom/selector-engine.js'
import { import {
defineJQueryPlugin, getElement, isDisabled, isVisible getElement, isDisabled, isVisible
} from './util/index.js' } from './util/index.js'
/** /**
@ -259,22 +259,6 @@ class ScrollSpy extends BaseComponent {
} }
} }
// Static
static jQueryInterface(config) {
return this.each(function () {
const data = ScrollSpy.getOrCreateInstance(this, config)
if (typeof config !== 'string') {
return
}
if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {
throw new TypeError(`No method named "${config}"`)
}
data[config]()
})
}
} }
/** /**
@ -287,10 +271,4 @@ EventHandler.on(window, EVENT_LOAD_DATA_API, () => {
} }
}) })
/**
* jQuery
*/
defineJQueryPlugin(ScrollSpy)
export default ScrollSpy export default ScrollSpy

View File

@ -8,7 +8,7 @@
import BaseComponent from './base-component.js' import BaseComponent from './base-component.js'
import EventHandler from './dom/event-handler.js' import EventHandler from './dom/event-handler.js'
import SelectorEngine from './dom/selector-engine.js' import SelectorEngine from './dom/selector-engine.js'
import { defineJQueryPlugin, getNextActiveElement, isDisabled } from './util/index.js' import { getNextActiveElement, isDisabled } from './util/index.js'
/** /**
* Constants * Constants
@ -263,23 +263,6 @@ class Tab extends BaseComponent {
_getOuterElement(elem) { _getOuterElement(elem) {
return elem.closest(SELECTOR_OUTER) || elem return elem.closest(SELECTOR_OUTER) || elem
} }
// Static
static jQueryInterface(config) {
return this.each(function () {
const data = Tab.getOrCreateInstance(this)
if (typeof config !== 'string') {
return
}
if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {
throw new TypeError(`No method named "${config}"`)
}
data[config]()
})
}
} }
/** /**
@ -306,10 +289,5 @@ EventHandler.on(window, EVENT_LOAD_DATA_API, () => {
Tab.getOrCreateInstance(element) Tab.getOrCreateInstance(element)
} }
}) })
/**
* jQuery
*/
defineJQueryPlugin(Tab)
export default Tab export default Tab

View File

@ -8,7 +8,7 @@
import BaseComponent from './base-component.js' import BaseComponent from './base-component.js'
import EventHandler from './dom/event-handler.js' import EventHandler from './dom/event-handler.js'
import { enableDismissTrigger } from './util/component-functions.js' import { enableDismissTrigger } from './util/component-functions.js'
import { defineJQueryPlugin, reflow } from './util/index.js' import { reflow } from './util/index.js'
/** /**
* Constants * Constants
@ -193,21 +193,6 @@ class Toast extends BaseComponent {
clearTimeout(this._timeout) clearTimeout(this._timeout)
this._timeout = null this._timeout = null
} }
// Static
static jQueryInterface(config) {
return this.each(function () {
const data = Toast.getOrCreateInstance(this, config)
if (typeof config === 'string') {
if (typeof data[config] === 'undefined') {
throw new TypeError(`No method named "${config}"`)
}
data[config](this)
}
})
}
} }
/** /**
@ -216,10 +201,4 @@ class Toast extends BaseComponent {
enableDismissTrigger(Toast) enableDismissTrigger(Toast)
/**
* jQuery
*/
defineJQueryPlugin(Toast)
export default Toast export default Toast

View File

@ -10,7 +10,7 @@ import BaseComponent from './base-component.js'
import EventHandler from './dom/event-handler.js' import EventHandler from './dom/event-handler.js'
import Manipulator from './dom/manipulator.js' import Manipulator from './dom/manipulator.js'
import { import {
defineJQueryPlugin, execute, findShadowRoot, getElement, getUID, isRTL, noop execute, findShadowRoot, getElement, getUID, isRTL, noop
} from './util/index.js' } from './util/index.js'
import { DefaultAllowlist } from './util/sanitizer.js' import { DefaultAllowlist } from './util/sanitizer.js'
import TemplateFactory from './util/template-factory.js' import TemplateFactory from './util/template-factory.js'
@ -604,29 +604,5 @@ class Tooltip extends BaseComponent {
this.tip = null this.tip = null
} }
} }
// Static
static jQueryInterface(config) {
return this.each(function () {
const data = Tooltip.getOrCreateInstance(this, config)
if (typeof config !== 'string') {
return
}
if (typeof data[config] === 'undefined') {
throw new TypeError(`No method named "${config}"`)
}
data[config]()
})
}
} }
/**
* jQuery
*/
defineJQueryPlugin(Tooltip)
export default Tooltip export default Tooltip

View File

@ -176,14 +176,6 @@ const reflow = element => {
element.offsetHeight // eslint-disable-line no-unused-expressions element.offsetHeight // eslint-disable-line no-unused-expressions
} }
const getjQuery = () => {
if (window.jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {
return window.jQuery
}
return null
}
const DOMContentLoadedCallbacks = [] const DOMContentLoadedCallbacks = []
const onDOMContentLoaded = callback => { const onDOMContentLoaded = callback => {
@ -205,23 +197,6 @@ const onDOMContentLoaded = callback => {
const isRTL = () => document.documentElement.dir === 'rtl' const isRTL = () => document.documentElement.dir === 'rtl'
const defineJQueryPlugin = plugin => {
onDOMContentLoaded(() => {
const $ = getjQuery()
/* istanbul ignore if */
if ($) {
const name = plugin.NAME
const JQUERY_NO_CONFLICT = $.fn[name]
$.fn[name] = plugin.jQueryInterface
$.fn[name].Constructor = plugin
$.fn[name].noConflict = () => {
$.fn[name] = JQUERY_NO_CONFLICT
return plugin.jQueryInterface
}
}
})
}
const execute = (possibleCallback, args = [], defaultValue = possibleCallback) => { const execute = (possibleCallback, args = [], defaultValue = possibleCallback) => {
return typeof possibleCallback === 'function' ? possibleCallback.call(...args) : defaultValue return typeof possibleCallback === 'function' ? possibleCallback.call(...args) : defaultValue
} }
@ -284,12 +259,10 @@ const getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed
} }
export { export {
defineJQueryPlugin,
execute, execute,
executeAfterTransition, executeAfterTransition,
findShadowRoot, findShadowRoot,
getElement, getElement,
getjQuery,
getNextActiveElement, getNextActiveElement,
getTransitionDurationFromElement, getTransitionDurationFromElement,
getUID, getUID,

View File

@ -261,47 +261,6 @@ const tooltip = new bootstrap.Tooltip(yourTooltipEl, {
}) })
``` ```
## Optionally using jQuery
**You don't need jQuery in Bootstrap 5**, but it's still possible to use our components with jQuery. If Bootstrap detects `jQuery` in the `window` object, it'll add all of our components in jQuery's plugin system. This allows you to do the following:
```js
// to enable tooltips with the default configuration
$('[data-bs-toggle="tooltip"]').tooltip()
// to initialize tooltips with given configuration
$('[data-bs-toggle="tooltip"]').tooltip({
boundary: 'clippingParents',
customClass: 'myClass'
})
// to trigger the `show` method
$('#myTooltip').tooltip('show')
```
The same goes for our other components.
### No conflict
Sometimes it is necessary to use Bootstrap plugins with other UI frameworks. In these circumstances, namespace collisions can occasionally occur. If this happens, you may call `.noConflict` on the plugin you wish to revert the value of.
```js
const bootstrapButton = $.fn.button.noConflict() // return $.fn.button to previously assigned value
$.fn.bootstrapBtn = bootstrapButton // give $().bootstrapBtn the Bootstrap functionality
```
Bootstrap does not officially support third-party JavaScript libraries like Prototype or jQuery UI. Despite `.noConflict` and namespaced events, there may be compatibility problems that you need to fix on your own.
### jQuery events
Bootstrap will detect jQuery if `jQuery` is present in the `window` object and there is no `data-bs-no-jquery` attribute set on `<body>`. If jQuery is found, Bootstrap will emit events thanks to jQuery's event system. So if you want to listen to Bootstrap's events, you'll have to use the jQuery methods (`.on`, `.one`) instead of `addEventListener`.
```js
$('#myTab a').on('shown.bs.tab', () => {
// do something...
})
```
## Disabled JavaScript ## Disabled JavaScript
Bootstrap's plugins have no special fallback when JavaScript is disabled. If you care about the user experience in this case, use [`<noscript>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/noscript) to explain the situation (and how to re-enable JavaScript) to your users, and/or add your own custom fallbacks. Bootstrap's plugins have no special fallback when JavaScript is disabled. If you care about the user experience in this case, use [`<noscript>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/noscript) to explain the situation (and how to re-enable JavaScript) to your users, and/or add your own custom fallbacks.