0
0
mirror of https://github.com/twbs/bootstrap.git synced 2025-02-23 20:54:22 +01:00

Remove jQuery support in plugins

This commit is contained in:
Mark Otto 2025-02-21 21:06:51 -08:00
parent 882cb55b50
commit eda99074f8
16 changed files with 8 additions and 386 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)
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) 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

@ -177,7 +177,7 @@ Note that for security reasons the `sanitize`, `sanitizeFn`, and `allowList` opt
| `popperConfig` | null, object, function | `null` | To change Bootstrap's default Popper config, see [Popper's configuration](https://popper.js.org/docs/v2/constructors/#options). When a function is used to create the Popper configuration, it's called with an object that contains the Bootstrap's default Popper configuration. It helps you use and merge the default with your own configuration. The function must return a configuration object for Popper. | | `popperConfig` | null, object, function | `null` | To change Bootstrap's default Popper config, see [Popper's configuration](https://popper.js.org/docs/v2/constructors/#options). When a function is used to create the Popper configuration, it's called with an object that contains the Bootstrap's default Popper configuration. It helps you use and merge the default with your own configuration. The function must return a configuration object for Popper. |
| `sanitize` | boolean | `true` | Enable or disable the sanitization. If activated `'template'`, `'content'` and `'title'` options will be sanitized. | | `sanitize` | boolean | `true` | Enable or disable the sanitization. If activated `'template'`, `'content'` and `'title'` options will be sanitized. |
| `sanitizeFn` | null, function | `null` | Here you can supply your own sanitize function. This can be useful if you prefer to use a dedicated library to perform sanitization. | | `sanitizeFn` | null, function | `null` | Here you can supply your own sanitize function. This can be useful if you prefer to use a dedicated library to perform sanitization. |
| `selector` | string, false | `false` | If a selector is provided, popover objects will be delegated to the specified targets. In practice, this is used to also apply popovers to dynamically added DOM elements (`jQuery.on` support). See [this issue]([[config:repo]]/issues/4215) and [an informative example](https://codepen.io/Johann-S/pen/djJYPb). **Note**: `title` attribute must not be used as a selector. | | `selector` | string, false | `false` | If a selector is provided, popover objects will be delegated to the specified targets. In practice, this is used to also apply popovers to dynamically added DOM elements. See [this issue]([[config:repo]]/issues/4215) and [an informative example](https://codepen.io/Johann-S/pen/djJYPb). **Note**: `title` attribute must not be used as a selector. |
| `template` | string | `'<div class="popover" role="popover"><div class="popover-arrow"></div><div class="popover-inner"></div></div>'` | Base HTML to use when creating the popover. The popover's `title` will be injected into the `.popover-inner`. `.popover-arrow` will become the popover's arrow. The outermost wrapper element should have the `.popover` class and `role="popover"`. | | `template` | string | `'<div class="popover" role="popover"><div class="popover-arrow"></div><div class="popover-inner"></div></div>'` | Base HTML to use when creating the popover. The popover's `title` will be injected into the `.popover-inner`. `.popover-arrow` will become the popover's arrow. The outermost wrapper element should have the `.popover` class and `role="popover"`. |
| `title` | string, element, function | `''` | Default title value if `title` attribute isn't present. If a function is given, it will be called with its `this` reference set to the element that the popover is attached to. | | `title` | string, element, function | `''` | Default title value if `title` attribute isn't present. If a function is given, it will be called with its `this` reference set to the element that the popover is attached to. |
| `trigger` | string | `'hover focus'` | How popover is triggered: click, hover, focus, manual. You may pass multiple triggers; separate them with a space. `'manual'` indicates that the popover will be triggered programmatically via the `.popover('show')`, `.popover('hide')` and `.popover('toggle')` methods; this value cannot be combined with any other trigger. `'hover'` on its own will result in popovers that cannot be triggered via the keyboard, and should only be used if alternative methods for conveying the same information for keyboard users is present. | | `trigger` | string | `'hover focus'` | How popover is triggered: click, hover, focus, manual. You may pass multiple triggers; separate them with a space. `'manual'` indicates that the popover will be triggered programmatically via the `.popover('show')`, `.popover('hide')` and `.popover('toggle')` methods; this value cannot be combined with any other trigger. `'hover'` on its own will result in popovers that cannot be triggered via the keyboard, and should only be used if alternative methods for conveying the same information for keyboard users is present. |

View File

@ -292,47 +292,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.