mirror of
https://github.com/twbs/bootstrap.git
synced 2025-08-10 23:54:08 +02:00
Implement data-dismiss="toast"
to allow user to interact itself with the component (#27155)
This commit is contained in:
@@ -22,6 +22,7 @@ const Toast = (($) => {
|
|||||||
const JQUERY_NO_CONFLICT = $.fn[NAME]
|
const JQUERY_NO_CONFLICT = $.fn[NAME]
|
||||||
|
|
||||||
const Event = {
|
const Event = {
|
||||||
|
CLICK_DISMISS : `click.dismiss${EVENT_KEY}`,
|
||||||
HIDE : `hide${EVENT_KEY}`,
|
HIDE : `hide${EVENT_KEY}`,
|
||||||
HIDDEN : `hidden${EVENT_KEY}`,
|
HIDDEN : `hidden${EVENT_KEY}`,
|
||||||
SHOW : `show${EVENT_KEY}`,
|
SHOW : `show${EVENT_KEY}`,
|
||||||
@@ -49,6 +50,10 @@ const Toast = (($) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Selector = {
|
||||||
|
DATA_DISMISS : '[data-dismiss="toast"]'
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ------------------------------------------------------------------------
|
* ------------------------------------------------------------------------
|
||||||
* Class Definition
|
* Class Definition
|
||||||
@@ -60,6 +65,7 @@ const Toast = (($) => {
|
|||||||
this._element = element
|
this._element = element
|
||||||
this._config = this._getConfig(config)
|
this._config = this._getConfig(config)
|
||||||
this._timeout = null
|
this._timeout = null
|
||||||
|
this._setListeners()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Getters
|
// Getters
|
||||||
@@ -104,31 +110,21 @@ const Toast = (($) => {
|
|||||||
}, this._config.delay.show)
|
}, this._config.delay.show)
|
||||||
}
|
}
|
||||||
|
|
||||||
hide() {
|
hide(withoutTimeout) {
|
||||||
if (!this._element.classList.contains(ClassName.SHOW)) {
|
if (!this._element.classList.contains(ClassName.SHOW)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
$(this._element).trigger(Event.HIDE)
|
$(this._element).trigger(Event.HIDE)
|
||||||
|
|
||||||
const complete = () => {
|
if (withoutTimeout) {
|
||||||
$(this._element).trigger(Event.HIDDEN)
|
this._close()
|
||||||
}
|
|
||||||
|
|
||||||
this._timeout = setTimeout(() => {
|
|
||||||
this._element.classList.remove(ClassName.SHOW)
|
|
||||||
|
|
||||||
if (this._config.animation) {
|
|
||||||
const transitionDuration = Util.getTransitionDurationFromElement(this._element)
|
|
||||||
|
|
||||||
$(this._element)
|
|
||||||
.one(Util.TRANSITION_END, complete)
|
|
||||||
.emulateTransitionEnd(transitionDuration)
|
|
||||||
} else {
|
} else {
|
||||||
complete()
|
this._timeout = setTimeout(() => {
|
||||||
}
|
this._close()
|
||||||
}, this._config.delay.hide)
|
}, this._config.delay.hide)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dispose() {
|
dispose() {
|
||||||
clearTimeout(this._timeout)
|
clearTimeout(this._timeout)
|
||||||
@@ -138,6 +134,8 @@ const Toast = (($) => {
|
|||||||
this._element.classList.remove(ClassName.SHOW)
|
this._element.classList.remove(ClassName.SHOW)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$(this._element).off(Event.CLICK_DISMISS)
|
||||||
|
|
||||||
$.removeData(this._element, DATA_KEY)
|
$.removeData(this._element, DATA_KEY)
|
||||||
this._element = null
|
this._element = null
|
||||||
this._config = null
|
this._config = null
|
||||||
@@ -168,6 +166,32 @@ const Toast = (($) => {
|
|||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_setListeners() {
|
||||||
|
$(this._element).on(
|
||||||
|
Event.CLICK_DISMISS,
|
||||||
|
Selector.DATA_DISMISS,
|
||||||
|
() => this.hide(true)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
_close() {
|
||||||
|
const complete = () => {
|
||||||
|
$(this._element).trigger(Event.HIDDEN)
|
||||||
|
}
|
||||||
|
|
||||||
|
this._element.classList.remove(ClassName.SHOW)
|
||||||
|
|
||||||
|
if (this._config.animation) {
|
||||||
|
const transitionDuration = Util.getTransitionDurationFromElement(this._element)
|
||||||
|
|
||||||
|
$(this._element)
|
||||||
|
.one(Util.TRANSITION_END, complete)
|
||||||
|
.emulateTransitionEnd(transitionDuration)
|
||||||
|
} else {
|
||||||
|
complete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Static
|
// Static
|
||||||
|
|
||||||
static _jQueryInterface(config) {
|
static _jQueryInterface(config) {
|
||||||
|
@@ -232,4 +232,33 @@ $(function () {
|
|||||||
})
|
})
|
||||||
.bootstrapToast('show')
|
.bootstrapToast('show')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
QUnit.test('should close toast when close element with data-dismiss attribute is set', function (assert) {
|
||||||
|
assert.expect(2)
|
||||||
|
var done = assert.async()
|
||||||
|
|
||||||
|
var toastHtml =
|
||||||
|
'<div class="toast" data-delay="1" data-autohide="false" data-animation="false">' +
|
||||||
|
'<button type="button" class="ml-2 mb-1 close" data-dismiss="toast">' +
|
||||||
|
'close' +
|
||||||
|
'</button>' +
|
||||||
|
'</div>'
|
||||||
|
|
||||||
|
var $toast = $(toastHtml)
|
||||||
|
.bootstrapToast()
|
||||||
|
.appendTo($('#qunit-fixture'))
|
||||||
|
|
||||||
|
$toast
|
||||||
|
.on('shown.bs.toast', function () {
|
||||||
|
assert.strictEqual($toast.hasClass('show'), true)
|
||||||
|
var button = $toast.find('.close')
|
||||||
|
button.trigger('click')
|
||||||
|
})
|
||||||
|
.on('hidden.bs.toast', function () {
|
||||||
|
assert.strictEqual($toast.hasClass('show'), false)
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
.bootstrapToast('show')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@@ -26,22 +26,28 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="notifications">
|
<div class="notifications">
|
||||||
<div id="toastAutoHide" class="toast">
|
<div id="toastAutoHide" class="toast" role="alert" aria-live="assertive" aria-atomic="true">
|
||||||
<div class="toast-header">
|
<div class="toast-header">
|
||||||
<img class="rounded mr-2" data-src="holder.js/20x20?size=1&text=.&bg=#007aff" alt="">
|
<img class="rounded mr-2" data-src="holder.js/20x20?size=1&text=.&bg=#007aff" alt="">
|
||||||
<strong class="mr-auto">Bootstrap</strong>
|
<strong class="mr-auto">Bootstrap</strong>
|
||||||
<small>11 mins ago</small>
|
<small>11 mins ago</small>
|
||||||
|
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="toast-body">
|
<div class="toast-body">
|
||||||
Hello, world! This is a toast message with <strong>autohide</strong> in 2 seconds
|
Hello, world! This is a toast message with <strong>autohide</strong> in 2 seconds
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="toast" data-autohide="false">
|
<div class="toast" data-autohide="false" role="alert" aria-live="assertive" aria-atomic="true">
|
||||||
<div class="toast-header">
|
<div class="toast-header">
|
||||||
<img class="rounded mr-2" data-src="holder.js/20x20?size=1&text=.&bg=#007aff" alt="">
|
<img class="rounded mr-2" data-src="holder.js/20x20?size=1&text=.&bg=#007aff" alt="">
|
||||||
<strong class="mr-auto">Bootstrap</strong>
|
<strong class="mr-auto">Bootstrap</strong>
|
||||||
<small class="text-muted">2 seconds ago</small>
|
<small class="text-muted">2 seconds ago</small>
|
||||||
|
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="toast-body">
|
<div class="toast-body">
|
||||||
Heads up, toasts will stack automatically
|
Heads up, toasts will stack automatically
|
||||||
|
@@ -24,11 +24,14 @@ A basic toast can include a header (though it doesn't strictly need one) with wh
|
|||||||
|
|
||||||
<div class="bg-light">
|
<div class="bg-light">
|
||||||
{% capture example %}
|
{% capture example %}
|
||||||
<div class="toast">
|
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true">
|
||||||
<div class="toast-header">
|
<div class="toast-header">
|
||||||
<img class="rounded mr-2" data-src="holder.js/20x20?size=1&text=.&bg=#007aff" alt="">
|
<img class="rounded mr-2" data-src="holder.js/20x20?size=1&text=.&bg=#007aff" alt="">
|
||||||
<strong class="mr-auto">Bootstrap</strong>
|
<strong class="mr-auto">Bootstrap</strong>
|
||||||
<small>11 mins ago</small>
|
<small>11 mins ago</small>
|
||||||
|
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="toast-body">
|
<div class="toast-body">
|
||||||
Hello, world! This is a toast message.
|
Hello, world! This is a toast message.
|
||||||
@@ -42,11 +45,14 @@ They're slightly translucent, too, so they blend over whatever they might appear
|
|||||||
|
|
||||||
<div class="bg-dark">
|
<div class="bg-dark">
|
||||||
{% capture example %}
|
{% capture example %}
|
||||||
<div class="toast">
|
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true">
|
||||||
<div class="toast-header">
|
<div class="toast-header">
|
||||||
<img class="rounded mr-2" data-src="holder.js/20x20?size=1&text=.&bg=#007aff" alt="">
|
<img class="rounded mr-2" data-src="holder.js/20x20?size=1&text=.&bg=#007aff" alt="">
|
||||||
<strong class="mr-auto">Bootstrap</strong>
|
<strong class="mr-auto">Bootstrap</strong>
|
||||||
<small class="text-muted">11 mins ago</small>
|
<small class="text-muted">11 mins ago</small>
|
||||||
|
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="toast-body">
|
<div class="toast-body">
|
||||||
Hello, world! This is a toast message.
|
Hello, world! This is a toast message.
|
||||||
@@ -60,22 +66,28 @@ Plus, they'll easily stack.
|
|||||||
|
|
||||||
<div class="bg-light">
|
<div class="bg-light">
|
||||||
{% capture example %}
|
{% capture example %}
|
||||||
<div class="toast">
|
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true">
|
||||||
<div class="toast-header">
|
<div class="toast-header">
|
||||||
<img class="rounded mr-2" data-src="holder.js/20x20?size=1&text=.&bg=#007aff" alt="">
|
<img class="rounded mr-2" data-src="holder.js/20x20?size=1&text=.&bg=#007aff" alt="">
|
||||||
<strong class="mr-auto">Bootstrap</strong>
|
<strong class="mr-auto">Bootstrap</strong>
|
||||||
<small class="text-muted">just now</small>
|
<small class="text-muted">just now</small>
|
||||||
|
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="toast-body">
|
<div class="toast-body">
|
||||||
See? Just like this.
|
See? Just like this.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="toast">
|
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true">
|
||||||
<div class="toast-header">
|
<div class="toast-header">
|
||||||
<img class="rounded mr-2" data-src="holder.js/20x20?size=1&text=.&bg=#007aff" alt="">
|
<img class="rounded mr-2" data-src="holder.js/20x20?size=1&text=.&bg=#007aff" alt="">
|
||||||
<strong class="mr-auto">Bootstrap</strong>
|
<strong class="mr-auto">Bootstrap</strong>
|
||||||
<small class="text-muted">2 seconds ago</small>
|
<small class="text-muted">2 seconds ago</small>
|
||||||
|
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="toast-body">
|
<div class="toast-body">
|
||||||
Heads up, toasts will stack automatically
|
Heads up, toasts will stack automatically
|
||||||
@@ -88,10 +100,12 @@ Plus, they'll easily stack.
|
|||||||
## Accessibility
|
## Accessibility
|
||||||
|
|
||||||
Toasts are intended to be small interruptions to your visitors or users, so to help those on screen readers, you should wrap your toasts in an [`aria-live` region](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Live_Regions). This allows screen readers the ability to see suggested interruptions without any visual cues.
|
Toasts are intended to be small interruptions to your visitors or users, so to help those on screen readers, you should wrap your toasts in an [`aria-live` region](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Live_Regions). This allows screen readers the ability to see suggested interruptions without any visual cues.
|
||||||
|
To improve accessibility level, we strongly recomend to use `autohide: false` and add a `close` button into the header to let user dismiss that element.
|
||||||
|
You also need to adapt the `role` and `aria-live` level depending on the content. If it's an important message like error, use an `alert` role `assertive` otherwise use a role `status` with a `polite` level.
|
||||||
|
|
||||||
{% highlight html %}
|
{% highlight html %}
|
||||||
<div role="region" aria-live="polite">
|
<div role="alert" aria-live="assertive" aria-atomic="true">
|
||||||
<div class="toast">...</div>
|
<div role="alert" aria-live="assertive" aria-atomic="true">...</div>
|
||||||
</div>
|
</div>
|
||||||
{% endhighlight %}
|
{% endhighlight %}
|
||||||
|
|
||||||
@@ -107,6 +121,9 @@ Place toasts with custom CSS as you need them. The top right is often used for n
|
|||||||
<img class="rounded mr-2" data-src="holder.js/20x20?size=1&text=.&bg=#007aff" alt="">
|
<img class="rounded mr-2" data-src="holder.js/20x20?size=1&text=.&bg=#007aff" alt="">
|
||||||
<strong class="mr-auto">Bootstrap</strong>
|
<strong class="mr-auto">Bootstrap</strong>
|
||||||
<small>11 mins ago</small>
|
<small>11 mins ago</small>
|
||||||
|
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="toast-body">
|
<div class="toast-body">
|
||||||
Hello, world! This is a toast message.
|
Hello, world! This is a toast message.
|
||||||
@@ -126,22 +143,28 @@ For systems that generate more notifications, consider using a wrapping element
|
|||||||
<div style="position: absolute; top: 0; right: 0;">
|
<div style="position: absolute; top: 0; right: 0;">
|
||||||
|
|
||||||
<!-- Then put toasts within -->
|
<!-- Then put toasts within -->
|
||||||
<div class="toast">
|
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true">
|
||||||
<div class="toast-header">
|
<div class="toast-header">
|
||||||
<img class="rounded mr-2" data-src="holder.js/20x20?size=1&text=.&bg=#007aff" alt="">
|
<img class="rounded mr-2" data-src="holder.js/20x20?size=1&text=.&bg=#007aff" alt="">
|
||||||
<strong class="mr-auto">Bootstrap</strong>
|
<strong class="mr-auto">Bootstrap</strong>
|
||||||
<small class="text-muted">just now</small>
|
<small class="text-muted">just now</small>
|
||||||
|
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="toast-body">
|
<div class="toast-body">
|
||||||
See? Just like this.
|
See? Just like this.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="toast">
|
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true">
|
||||||
<div class="toast-header">
|
<div class="toast-header">
|
||||||
<img class="rounded mr-2" data-src="holder.js/20x20?size=1&text=.&bg=#007aff" alt="">
|
<img class="rounded mr-2" data-src="holder.js/20x20?size=1&text=.&bg=#007aff" alt="">
|
||||||
<strong class="mr-auto">Bootstrap</strong>
|
<strong class="mr-auto">Bootstrap</strong>
|
||||||
<small class="text-muted">2 seconds ago</small>
|
<small class="text-muted">2 seconds ago</small>
|
||||||
|
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="toast-body">
|
<div class="toast-body">
|
||||||
Heads up, toasts will stack automatically
|
Heads up, toasts will stack automatically
|
||||||
@@ -162,11 +185,14 @@ You can also get fancy with flexbox utilities.
|
|||||||
<div class="d-flex justify-content-center" style="position: absolute; top: 0; right: 0; left: 0;">
|
<div class="d-flex justify-content-center" style="position: absolute; top: 0; right: 0; left: 0;">
|
||||||
|
|
||||||
<!-- Then put toasts within -->
|
<!-- Then put toasts within -->
|
||||||
<div class="toast">
|
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true">
|
||||||
<div class="toast-header">
|
<div class="toast-header">
|
||||||
<img class="rounded mr-2" data-src="holder.js/20x20?size=1&text=.&bg=#007aff" alt="">
|
<img class="rounded mr-2" data-src="holder.js/20x20?size=1&text=.&bg=#007aff" alt="">
|
||||||
<strong class="mr-auto">Bootstrap</strong>
|
<strong class="mr-auto">Bootstrap</strong>
|
||||||
<small>11 mins ago</small>
|
<small>11 mins ago</small>
|
||||||
|
<button type="button" class="close" data-dismiss="toast" aria-label="Close" style="">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="toast-body">
|
<div class="toast-body">
|
||||||
Hello, world! This is a toast message.
|
Hello, world! This is a toast message.
|
||||||
|
Reference in New Issue
Block a user