const JQUERY_NO_CONFLICT = $.fn[NAME]
const Event = {
- HIDE : `hide${EVENT_KEY}`,
- HIDDEN : `hidden${EVENT_KEY}`,
- SHOW : `show${EVENT_KEY}`,
- SHOWN : `shown${EVENT_KEY}`
+ CLICK_DISMISS : `click.dismiss${EVENT_KEY}`,
+ HIDE : `hide${EVENT_KEY}`,
+ HIDDEN : `hidden${EVENT_KEY}`,
+ SHOW : `show${EVENT_KEY}`,
+ SHOWN : `shown${EVENT_KEY}`
}
const ClassName = {
}
}
+ const Selector = {
+ DATA_DISMISS : '[data-dismiss="toast"]'
+ }
+
/**
* ------------------------------------------------------------------------
* Class Definition
this._element = element
this._config = this._getConfig(config)
this._timeout = null
+ this._setListeners()
}
// Getters
}, this._config.delay.show)
}
- hide() {
+ hide(withoutTimeout) {
if (!this._element.classList.contains(ClassName.SHOW)) {
return
}
$(this._element).trigger(Event.HIDE)
- const complete = () => {
- $(this._element).trigger(Event.HIDDEN)
+ if (withoutTimeout) {
+ this._close()
+ } else {
+ this._timeout = setTimeout(() => {
+ this._close()
+ }, this._config.delay.hide)
}
-
- 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 {
- complete()
- }
- }, this._config.delay.hide)
}
dispose() {
this._element.classList.remove(ClassName.SHOW)
}
+ $(this._element).off(Event.CLICK_DISMISS)
+
$.removeData(this._element, DATA_KEY)
this._element = null
this._config = null
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 _jQueryInterface(config) {
})
.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')
+ })
})
</div>
<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">
<img class="rounded mr-2" data-src="holder.js/20x20?size=1&text=.&bg=#007aff" alt="">
<strong class="mr-auto">Bootstrap</strong>
<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 class="toast-body">
Hello, world! This is a toast message with <strong>autohide</strong> in 2 seconds
</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">
<img class="rounded mr-2" data-src="holder.js/20x20?size=1&text=.&bg=#007aff" alt="">
<strong class="mr-auto">Bootstrap</strong>
<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 class="toast-body">
Heads up, toasts will stack automatically
<div class="bg-light">
{% capture example %}
-<div class="toast">
+<div class="toast" role="alert" aria-live="assertive" aria-atomic="true">
<div class="toast-header">
<img class="rounded mr-2" data-src="holder.js/20x20?size=1&text=.&bg=#007aff" alt="">
<strong class="mr-auto">Bootstrap</strong>
<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 class="toast-body">
Hello, world! This is a toast message.
<div class="bg-dark">
{% capture example %}
-<div class="toast">
+<div class="toast" role="alert" aria-live="assertive" aria-atomic="true">
<div class="toast-header">
<img class="rounded mr-2" data-src="holder.js/20x20?size=1&text=.&bg=#007aff" alt="">
<strong class="mr-auto">Bootstrap</strong>
<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 class="toast-body">
Hello, world! This is a toast message.
<div class="bg-light">
{% capture example %}
-<div class="toast">
+<div class="toast" role="alert" aria-live="assertive" aria-atomic="true">
<div class="toast-header">
<img class="rounded mr-2" data-src="holder.js/20x20?size=1&text=.&bg=#007aff" alt="">
<strong class="mr-auto">Bootstrap</strong>
<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 class="toast-body">
See? Just like this.
</div>
</div>
-<div class="toast">
+<div class="toast" role="alert" aria-live="assertive" aria-atomic="true">
<div class="toast-header">
<img class="rounded mr-2" data-src="holder.js/20x20?size=1&text=.&bg=#007aff" alt="">
<strong class="mr-auto">Bootstrap</strong>
<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 class="toast-body">
Heads up, toasts will stack automatically
## 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.
+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 %}
-<div role="region" aria-live="polite">
- <div class="toast">...</div>
+<div role="alert" aria-live="assertive" aria-atomic="true">
+ <div role="alert" aria-live="assertive" aria-atomic="true">...</div>
</div>
{% endhighlight %}
<img class="rounded mr-2" data-src="holder.js/20x20?size=1&text=.&bg=#007aff" alt="">
<strong class="mr-auto">Bootstrap</strong>
<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 class="toast-body">
Hello, world! This is a toast message.
<div style="position: absolute; top: 0; right: 0;">
<!-- Then put toasts within -->
- <div class="toast">
+ <div class="toast" role="alert" aria-live="assertive" aria-atomic="true">
<div class="toast-header">
<img class="rounded mr-2" data-src="holder.js/20x20?size=1&text=.&bg=#007aff" alt="">
<strong class="mr-auto">Bootstrap</strong>
<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 class="toast-body">
See? Just like this.
</div>
</div>
- <div class="toast">
+ <div class="toast" role="alert" aria-live="assertive" aria-atomic="true">
<div class="toast-header">
<img class="rounded mr-2" data-src="holder.js/20x20?size=1&text=.&bg=#007aff" alt="">
<strong class="mr-auto">Bootstrap</strong>
<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 class="toast-body">
Heads up, toasts will stack automatically
<div class="d-flex justify-content-center" style="position: absolute; top: 0; right: 0; left: 0;">
<!-- Then put toasts within -->
- <div class="toast">
+ <div class="toast" role="alert" aria-live="assertive" aria-atomic="true">
<div class="toast-header">
<img class="rounded mr-2" data-src="holder.js/20x20?size=1&text=.&bg=#007aff" alt="">
<strong class="mr-auto">Bootstrap</strong>
<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 class="toast-body">
Hello, world! This is a toast message.