mirror of
https://github.com/twbs/bootstrap.git
synced 2025-02-19 16:54:24 +01:00
Fix JS components console error "Error: <Component> is transitioning"
This commit is contained in:
parent
ce0e2f8e76
commit
48c5efa4c3
7
docs/_includes/callout-danger-async-methods.md
Normal file
7
docs/_includes/callout-danger-async-methods.md
Normal file
@ -0,0 +1,7 @@
|
||||
{% callout danger %}
|
||||
#### Asynchronous methods and transitions
|
||||
|
||||
All API methods are **asynchronous** and start a **transition**. They returns to the caller as soon as the transition is started but **before it ends**. In addition, a method call on a **transitioning component will be ignored**.
|
||||
|
||||
[See our Javascript documentation for more informations.]({{ site.baseurl }}/getting-started/javascript/#content)
|
||||
{% endcallout %}
|
@ -229,6 +229,9 @@ Options can be passed via data attributes or JavaScript. For data attributes, ap
|
||||
|
||||
### Methods
|
||||
|
||||
{% capture callout-include %}{% include callout-danger-async-methods.md %}{% endcapture %}
|
||||
{{ callout-include | markdownify }}
|
||||
|
||||
#### `.carousel(options)`
|
||||
|
||||
Initializes the carousel with an optional options `object` and starts cycling through items.
|
||||
@ -249,15 +252,15 @@ Stops the carousel from cycling through items.
|
||||
|
||||
#### `.carousel(number)`
|
||||
|
||||
Cycles the carousel to a particular frame (0 based, similar to an array).
|
||||
Cycles the carousel to a particular frame (0 based, similar to an array). **Returns to the caller before the target item has been shown** (i.e. before the `slid.bs.carousel` event occurs).
|
||||
|
||||
#### `.carousel('prev')`
|
||||
|
||||
Cycles to the previous item.
|
||||
Cycles to the previous item. **Returns to the caller before the previous item has been shown** (i.e. before the `slid.bs.carousel` event occurs).
|
||||
|
||||
#### `.carousel('next')`
|
||||
|
||||
Cycles to the next item.
|
||||
Cycles to the next item. **Returns to the caller before the next item has been shown** (i.e. before the `slid.bs.carousel` event occurs).
|
||||
|
||||
### Events
|
||||
|
||||
|
@ -178,6 +178,9 @@ Options can be passed via data attributes or JavaScript. For data attributes, ap
|
||||
|
||||
### Methods
|
||||
|
||||
{% capture callout-include %}{% include callout-danger-async-methods.md %}{% endcapture %}
|
||||
{{ callout-include | markdownify }}
|
||||
|
||||
#### `.collapse(options)`
|
||||
|
||||
Activates your content as a collapsible element. Accepts an optional options `object`.
|
||||
@ -190,15 +193,15 @@ $('#myCollapsible').collapse({
|
||||
|
||||
#### `.collapse('toggle')`
|
||||
|
||||
Toggles a collapsible element to shown or hidden.
|
||||
Toggles a collapsible element to shown or hidden. **Returns to the caller before the collapsible element has actually been shown or hidden (i.e. before the `shown.bs.collapse` or `hidden.bs.collapse` event occurs).
|
||||
|
||||
#### `.collapse('show')`
|
||||
|
||||
Shows a collapsible element.
|
||||
Shows a collapsible element. **Returns to the caller before the collapsible element has actually been shown** (i.e. before the `shown.bs.collapse` event occurs).
|
||||
|
||||
#### `.collapse('hide')`
|
||||
|
||||
Hides a collapsible element.
|
||||
Hides a collapsible element. **Returns to the caller before the collapsible element has actually been hidden** (i.e. before the `hidden.bs.collapse` event occurs).
|
||||
|
||||
### Events
|
||||
|
||||
|
@ -547,6 +547,9 @@ Options can be passed via data attributes or JavaScript. For data attributes, ap
|
||||
|
||||
### Methods
|
||||
|
||||
{% capture callout-include %}{% include callout-danger-async-methods.md %}{% endcapture %}
|
||||
{{ callout-include | markdownify }}
|
||||
|
||||
#### `.modal(options)`
|
||||
|
||||
Activates your content as a modal. Accepts an optional options `object`.
|
||||
|
@ -404,6 +404,9 @@ To make tabs fade in, add `.fade` to each `.tab-pane`. The first tab pane must a
|
||||
|
||||
### Methods
|
||||
|
||||
{% capture callout-include %}{% include callout-danger-async-methods.md %}{% endcapture %}
|
||||
{{ callout-include | markdownify }}
|
||||
|
||||
#### $().tab
|
||||
|
||||
Activates a tab element and content container. Tab should have either a `data-target` or an `href` targeting a container node in the DOM.
|
||||
|
@ -277,6 +277,9 @@ Options for individual popovers can alternatively be specified through the use o
|
||||
|
||||
### Methods
|
||||
|
||||
{% capture callout-include %}{% include callout-danger-async-methods.md %}{% endcapture %}
|
||||
{{ callout-include | markdownify }}
|
||||
|
||||
#### `$().popover(options)`
|
||||
|
||||
Initializes popovers for an element collection.
|
||||
|
@ -254,6 +254,9 @@ Options for individual tooltips can alternatively be specified through the use o
|
||||
|
||||
### Methods
|
||||
|
||||
{% capture callout-include %}{% include callout-danger-async-methods.md %}{% endcapture %}
|
||||
{{ callout-include | markdownify }}
|
||||
|
||||
#### `$().tooltip(options)`
|
||||
|
||||
Attaches a tooltip handler to an element collection.
|
||||
|
@ -36,6 +36,18 @@ Alternatively, to target a specific plugin, just include the plugin's name as a
|
||||
$(document).off('.alert.data-api')
|
||||
{% endhighlight %}
|
||||
|
||||
## Events
|
||||
|
||||
Bootstrap provides custom events for most plugins' unique actions. Generally, these come in an infinitive and past participle form - where the infinitive (ex. `show`) is triggered at the start of an event, and its past participle form (ex. `shown`) is triggered on the completion of an action.
|
||||
|
||||
All infinitive events provide [`preventDefault()`](https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault) functionality. This provides the ability to stop the execution of an action before it starts.
|
||||
|
||||
{% highlight js %}
|
||||
$('#myModal').on('show.bs.modal', function (e) {
|
||||
if (!data) return e.preventDefault() // stops modal from being shown
|
||||
})
|
||||
{% endhighlight %}
|
||||
|
||||
## Programmatic API
|
||||
|
||||
We also believe you should be able to use all Bootstrap plugins purely through the JavaScript API. All public APIs are single, chainable methods, and return the collection acted upon.
|
||||
@ -54,11 +66,32 @@ $('#myModal').modal('show') // initializes and invokes show immed
|
||||
|
||||
Each plugin also exposes its raw constructor on a `Constructor` property: `$.fn.popover.Constructor`. If you'd like to get a particular plugin instance, retrieve it directly from an element: `$('[rel="popover"]').data('popover')`.
|
||||
|
||||
### Asynchronous functions and transitions
|
||||
|
||||
All programmatic API methods are **asynchronous** and returns to the caller once the transition is started but **before it ends**.
|
||||
|
||||
In order to execute an action once the transition is complete, you can listen to the corresponding event.
|
||||
{% highlight js %}
|
||||
$('#myCollapse').on('shown.bs.collapse', function (e) {
|
||||
// Action to execute once the collapsible area is expanded
|
||||
})
|
||||
{% endhighlight %}
|
||||
|
||||
In addition a method call on a **transitioning component will be ignored**.
|
||||
{% highlight js %}
|
||||
$('#myCarousel').on('slid.bs.carousel', function (e) {
|
||||
$('#myCarousel').carousel('2') // Will slide to the slide 2 as soon as the transition to slide 1 is finished
|
||||
})
|
||||
|
||||
$('#myCarousel').carousel('1') // Will start sliding to the slide 1 and returns to the caller
|
||||
$('#myCarousel').carousel('2') // !! Will be ignored, as the transition to the slide 1 is not finished !!
|
||||
{% endhighlight %}
|
||||
|
||||
### Default settings
|
||||
You can change the default settings for a plugin by modifying the plugin's `Constructor.DEFAULTS` object:
|
||||
You can change the default settings for a plugin by modifying the plugin's `Constructor.Default` object:
|
||||
|
||||
{% highlight js %}
|
||||
$.fn.modal.Constructor.DEFAULTS.keyboard = false // changes default for the modal plugin's `keyboard` option to false
|
||||
$.fn.modal.Constructor.Default.keyboard = false // changes default for the modal plugin's `keyboard` option to false
|
||||
{% endhighlight %}
|
||||
|
||||
## No conflict
|
||||
@ -70,18 +103,6 @@ var bootstrapButton = $.fn.button.noConflict() // return $.fn.button to previous
|
||||
$.fn.bootstrapBtn = bootstrapButton // give $().bootstrapBtn the Bootstrap functionality
|
||||
{% endhighlight %}
|
||||
|
||||
## Events
|
||||
|
||||
Bootstrap provides custom events for most plugins' unique actions. Generally, these come in an infinitive and past participle form - where the infinitive (ex. `show`) is triggered at the start of an event, and its past participle form (ex. `shown`) is triggered on the completion of an action.
|
||||
|
||||
All infinitive events provide [`preventDefault()`](https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault) functionality. This provides the ability to stop the execution of an action before it starts.
|
||||
|
||||
{% highlight js %}
|
||||
$('#myModal').on('show.bs.modal', function (e) {
|
||||
if (!data) return e.preventDefault() // stops modal from being shown
|
||||
})
|
||||
{% endhighlight %}
|
||||
|
||||
## Version numbers
|
||||
|
||||
The version of each of Bootstrap's jQuery plugins can be accessed via the `VERSION` property of the plugin's constructor. For example, for the tooltip plugin:
|
||||
@ -100,8 +121,8 @@ Bootstrap's plugins don't fall back particularly gracefully when JavaScript is d
|
||||
**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.
|
||||
{% endcallout %}
|
||||
|
||||
## Transitions
|
||||
## Util
|
||||
|
||||
For simple transition effects, include `transition.js` once alongside the other JS files. If you're using the compiled (or minified) `bootstrap.js`, there is no need to include this—it's already there.
|
||||
All Bootstrap Javascript depend on `util.js` and it has to be included alongside the other JS files. If you're using the compiled (or minified) `bootstrap.js`, there is no need to include this—it's already there.
|
||||
|
||||
Transition.js is a basic helper for `transitionEnd` events as well as a CSS transition emulator. It's used by the other plugins to check for CSS transition support and to catch hanging transitions.
|
||||
`util.js` includes utility functions and a basic helper for `transitionEnd` events as well as a CSS transition emulator. It's used by the other plugins to check for CSS transition support and to catch hanging transitions.
|
||||
|
@ -120,10 +120,9 @@ const Carousel = (($) => {
|
||||
// public
|
||||
|
||||
next() {
|
||||
if (this._isSliding) {
|
||||
throw new Error('Carousel is sliding')
|
||||
if (!this._isSliding) {
|
||||
this._slide(Direction.NEXT)
|
||||
}
|
||||
this._slide(Direction.NEXT)
|
||||
}
|
||||
|
||||
nextWhenVisible() {
|
||||
@ -134,10 +133,9 @@ const Carousel = (($) => {
|
||||
}
|
||||
|
||||
prev() {
|
||||
if (this._isSliding) {
|
||||
throw new Error('Carousel is sliding')
|
||||
if (!this._isSliding) {
|
||||
this._slide(Direction.PREV)
|
||||
}
|
||||
this._slide(Direction.PREV)
|
||||
}
|
||||
|
||||
pause(event) {
|
||||
|
@ -120,11 +120,8 @@ const Collapse = (($) => {
|
||||
}
|
||||
|
||||
show() {
|
||||
if (this._isTransitioning) {
|
||||
throw new Error('Collapse is transitioning')
|
||||
}
|
||||
|
||||
if ($(this._element).hasClass(ClassName.SHOW)) {
|
||||
if (this._isTransitioning ||
|
||||
$(this._element).hasClass(ClassName.SHOW)) {
|
||||
return
|
||||
}
|
||||
|
||||
@ -204,11 +201,8 @@ const Collapse = (($) => {
|
||||
}
|
||||
|
||||
hide() {
|
||||
if (this._isTransitioning) {
|
||||
throw new Error('Collapse is transitioning')
|
||||
}
|
||||
|
||||
if (!$(this._element).hasClass(ClassName.SHOW)) {
|
||||
if (this._isTransitioning ||
|
||||
!$(this._element).hasClass(ClassName.SHOW)) {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -87,7 +87,6 @@ const Modal = (($) => {
|
||||
this._isShown = false
|
||||
this._isBodyOverflowing = false
|
||||
this._ignoreBackdropClick = false
|
||||
this._isTransitioning = false
|
||||
this._originalBodyPadding = 0
|
||||
this._scrollbarWidth = 0
|
||||
}
|
||||
@ -112,13 +111,13 @@ const Modal = (($) => {
|
||||
|
||||
show(relatedTarget) {
|
||||
if (this._isTransitioning) {
|
||||
throw new Error('Modal is transitioning')
|
||||
return
|
||||
}
|
||||
|
||||
if (Util.supportsTransitionEnd() &&
|
||||
$(this._element).hasClass(ClassName.FADE)) {
|
||||
if (Util.supportsTransitionEnd() && $(this._element).hasClass(ClassName.FADE)) {
|
||||
this._isTransitioning = true
|
||||
}
|
||||
|
||||
const showEvent = $.Event(Event.SHOW, {
|
||||
relatedTarget
|
||||
})
|
||||
@ -161,17 +160,18 @@ const Modal = (($) => {
|
||||
event.preventDefault()
|
||||
}
|
||||
|
||||
if (this._isTransitioning) {
|
||||
throw new Error('Modal is transitioning')
|
||||
if (this._isTransitioning || !this._isShown) {
|
||||
return
|
||||
}
|
||||
|
||||
const transition = Util.supportsTransitionEnd() &&
|
||||
$(this._element).hasClass(ClassName.FADE)
|
||||
const transition = Util.supportsTransitionEnd() && $(this._element).hasClass(ClassName.FADE)
|
||||
|
||||
if (transition) {
|
||||
this._isTransitioning = true
|
||||
}
|
||||
|
||||
const hideEvent = $.Event(Event.HIDE)
|
||||
|
||||
$(this._element).trigger(hideEvent)
|
||||
|
||||
if (!this._isShown || hideEvent.isDefaultPrevented()) {
|
||||
@ -191,6 +191,7 @@ const Modal = (($) => {
|
||||
$(this._dialog).off(Event.MOUSEDOWN_DISMISS)
|
||||
|
||||
if (transition) {
|
||||
|
||||
$(this._element)
|
||||
.one(Util.TRANSITION_END, (event) => this._hideModal(event))
|
||||
.emulateTransitionEnd(TRANSITION_DURATION)
|
||||
@ -307,7 +308,7 @@ const Modal = (($) => {
|
||||
|
||||
_hideModal() {
|
||||
this._element.style.display = 'none'
|
||||
this._element.setAttribute('aria-hidden', 'true')
|
||||
this._element.setAttribute('aria-hidden', true)
|
||||
this._isTransitioning = false
|
||||
this._showBackdrop(() => {
|
||||
$(document.body).removeClass(ClassName.OPEN)
|
||||
|
@ -124,12 +124,11 @@ const Tooltip = (($) => {
|
||||
constructor(element, config) {
|
||||
|
||||
// private
|
||||
this._isEnabled = true
|
||||
this._timeout = 0
|
||||
this._hoverState = ''
|
||||
this._activeTrigger = {}
|
||||
this._isTransitioning = false
|
||||
this._tether = null
|
||||
this._isEnabled = true
|
||||
this._timeout = 0
|
||||
this._hoverState = ''
|
||||
this._activeTrigger = {}
|
||||
this._tether = null
|
||||
|
||||
// protected
|
||||
this.element = element
|
||||
@ -250,9 +249,6 @@ const Tooltip = (($) => {
|
||||
|
||||
const showEvent = $.Event(this.constructor.Event.SHOW)
|
||||
if (this.isWithContent() && this._isEnabled) {
|
||||
if (this._isTransitioning) {
|
||||
throw new Error('Tooltip is transitioning')
|
||||
}
|
||||
$(this.element).trigger(showEvent)
|
||||
|
||||
const isInTheDom = $.contains(
|
||||
@ -284,9 +280,11 @@ const Tooltip = (($) => {
|
||||
|
||||
const container = this.config.container === false ? document.body : $(this.config.container)
|
||||
|
||||
$(tip)
|
||||
.data(this.constructor.DATA_KEY, this)
|
||||
.appendTo(container)
|
||||
$(tip).data(this.constructor.DATA_KEY, this)
|
||||
|
||||
if (!$.contains(this.element.ownerDocument.documentElement, this.tip)) {
|
||||
$(tip).appendTo(container)
|
||||
}
|
||||
|
||||
$(this.element).trigger(this.constructor.Event.INSERTED)
|
||||
|
||||
@ -308,8 +306,7 @@ const Tooltip = (($) => {
|
||||
|
||||
const complete = () => {
|
||||
const prevHoverState = this._hoverState
|
||||
this._hoverState = null
|
||||
this._isTransitioning = false
|
||||
this._hoverState = null
|
||||
|
||||
$(this.element).trigger(this.constructor.Event.SHOWN)
|
||||
|
||||
@ -319,7 +316,6 @@ const Tooltip = (($) => {
|
||||
}
|
||||
|
||||
if (Util.supportsTransitionEnd() && $(this.tip).hasClass(ClassName.FADE)) {
|
||||
this._isTransitioning = true
|
||||
$(this.tip)
|
||||
.one(Util.TRANSITION_END, complete)
|
||||
.emulateTransitionEnd(Tooltip._TRANSITION_DURATION)
|
||||
@ -333,9 +329,6 @@ const Tooltip = (($) => {
|
||||
hide(callback) {
|
||||
const tip = this.getTipElement()
|
||||
const hideEvent = $.Event(this.constructor.Event.HIDE)
|
||||
if (this._isTransitioning) {
|
||||
throw new Error('Tooltip is transitioning')
|
||||
}
|
||||
const complete = () => {
|
||||
if (this._hoverState !== HoverState.SHOW && tip.parentNode) {
|
||||
tip.parentNode.removeChild(tip)
|
||||
@ -344,7 +337,6 @@ const Tooltip = (($) => {
|
||||
this._cleanTipClass()
|
||||
this.element.removeAttribute('aria-describedby')
|
||||
$(this.element).trigger(this.constructor.Event.HIDDEN)
|
||||
this._isTransitioning = false
|
||||
this.cleanupTether()
|
||||
|
||||
if (callback) {
|
||||
@ -366,7 +358,7 @@ const Tooltip = (($) => {
|
||||
|
||||
if (Util.supportsTransitionEnd() &&
|
||||
$(this.tip).hasClass(ClassName.FADE)) {
|
||||
this._isTransitioning = true
|
||||
|
||||
$(tip)
|
||||
.one(Util.TRANSITION_END, complete)
|
||||
.emulateTransitionEnd(TRANSITION_DURATION)
|
||||
|
@ -45,31 +45,11 @@
|
||||
<script src="../../dist/carousel.js"></script>
|
||||
|
||||
<script>
|
||||
// Should throw an error because carousel is in transition
|
||||
function testCarouselTransitionError() {
|
||||
var err = false
|
||||
var $carousel = $('#carousel-example-generic')
|
||||
$carousel.on('slid.bs.carousel', function () {
|
||||
$carousel.off('slid.bs.carousel')
|
||||
if (!err) {
|
||||
alert('No error thrown for : testCarouselTransitionError')
|
||||
}
|
||||
})
|
||||
try {
|
||||
$carousel.carousel('next').carousel('prev')
|
||||
}
|
||||
catch (e) {
|
||||
err = true
|
||||
console.error(e.message)
|
||||
}
|
||||
}
|
||||
|
||||
$(function () {
|
||||
$(function() {
|
||||
// Test to show that the carousel doesn't slide when the current tab isn't visible
|
||||
$('#carousel-example-generic').on('slid.bs.carousel', function (event) {
|
||||
$('#carousel-example-generic').on('slid.bs.carousel', function(event) {
|
||||
console.log('slid at ', event.timeStamp)
|
||||
})
|
||||
testCarouselTransitionError()
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
|
@ -60,30 +60,5 @@
|
||||
<script src="../../../docs/assets/js/vendor/jquery-slim.min.js"></script>
|
||||
<script src="../../dist/util.js"></script>
|
||||
<script src="../../dist/collapse.js"></script>
|
||||
<script>
|
||||
// JavaScript Test
|
||||
$(function () {
|
||||
testCollapseTransitionError()
|
||||
});
|
||||
|
||||
// Should throw an error because carousel is in transition
|
||||
function testCollapseTransitionError() {
|
||||
var err = false
|
||||
$('#collapseOne').on('hidden.bs.collapse', function (e) {
|
||||
$(this).off('hidden.bs.collapse')
|
||||
if (!err) {
|
||||
alert('No error thrown for : testCollapseTransitionError')
|
||||
}
|
||||
})
|
||||
|
||||
try {
|
||||
$('#collapseOne').collapse('hide').collapse('show')
|
||||
}
|
||||
catch (e) {
|
||||
err = true
|
||||
console.error(e.message)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -187,26 +187,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Should throw an error because modal is in transition
|
||||
function testModalTransitionError() {
|
||||
var err = false
|
||||
// Close #myModal
|
||||
$('#myModal').on('shown.bs.modal', function () {
|
||||
$('#myModal').modal('hide').off('shown.bs.modal')
|
||||
if (!err) {
|
||||
alert('No error thrown for : testModalTransitionError')
|
||||
}
|
||||
})
|
||||
|
||||
try {
|
||||
$('#myModal').modal('show').modal('hide')
|
||||
}
|
||||
catch (e) {
|
||||
err = true
|
||||
console.error(e.message)
|
||||
}
|
||||
}
|
||||
|
||||
$(function () {
|
||||
$('[data-toggle="popover"]').popover()
|
||||
$('[data-toggle="tooltip"]').tooltip()
|
||||
@ -219,7 +199,6 @@
|
||||
$('#firefoxModal').on('focus', reportFirefoxTestResult.bind(false))
|
||||
$('#ff-bug-input').on('focus', reportFirefoxTestResult.bind(true))
|
||||
})
|
||||
testModalTransitionError()
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
|
@ -41,26 +41,7 @@
|
||||
<script>
|
||||
$(function () {
|
||||
$('[data-toggle="tooltip"]').tooltip()
|
||||
testTooltipTransitionError()
|
||||
})
|
||||
|
||||
// Should throw an error because tooltip is in transition
|
||||
function testTooltipTransitionError() {
|
||||
var err = false
|
||||
$('#btnOne').on('shown.bs.tooltip', function () {
|
||||
$('#btnOne').tooltip('hide').off('shown.bs.tooltip')
|
||||
if (!err) {
|
||||
alert('No error thrown for : testTooltipTransitionError')
|
||||
}
|
||||
})
|
||||
try {
|
||||
$('#btnOne').tooltip('show').tooltip('hide')
|
||||
}
|
||||
catch (e) {
|
||||
err = true
|
||||
console.error(e.message)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
Loading…
x
Reference in New Issue
Block a user