const Event = {
HIDE: `hide${EVENT_KEY}`,
+ HIDE_PREVENTED: `hidePrevented${EVENT_KEY}`,
HIDDEN: `hidden${EVENT_KEY}`,
SHOW: `show${EVENT_KEY}`,
SHOWN: `shown${EVENT_KEY}`,
BACKDROP: 'modal-backdrop',
OPEN: 'modal-open',
FADE: 'fade',
- SHOW: 'show'
+ SHOW: 'show',
+ STATIC: 'modal-static'
}
const Selector = {
if (this._isShown && this._config.keyboard) {
EventHandler.on(this._element, Event.KEYDOWN_DISMISS, event => {
if (event.which === ESCAPE_KEYCODE) {
- event.preventDefault()
- this.hide()
+ this._triggerBackdropTransition()
}
})
} else {
return
}
- if (this._config.backdrop === 'static') {
- this._element.focus()
- } else {
- this.hide()
- }
+ this._triggerBackdropTransition()
})
if (animate) {
}
}
+ _triggerBackdropTransition() {
+ if (this._config.backdrop === 'static') {
+ const hideEvent = EventHandler.trigger(this._element, Event.HIDE_PREVENTED)
+ if (hideEvent.defaultPrevented) {
+ return
+ }
+
+ this._element.classList.add(ClassName.STATIC)
+ const modalTransitionDuration = getTransitionDurationFromElement(this._element)
+ EventHandler.one(this._element, TRANSITION_END, () => {
+ this._element.classList.remove(ClassName.STATIC)
+ })
+ emulateTransitionEnd(this._element, modalTransitionDuration)
+ this._element.focus()
+ } else {
+ this.hide()
+ }
+ }
+
// ----------------------------------------------------------------------
// the following methods are used to handle overflowing modals
// ----------------------------------------------------------------------
modal.show()
})
+ it('should not close modal when clicking outside of modal-content if backdrop = static', done => {
+ fixtureEl.innerHTML = '<div class="modal" data-backdrop="static" ><div class="modal-dialog" /></div>'
+
+ const modalEl = fixtureEl.querySelector('.modal')
+ const modal = new Modal(modalEl, {
+ backdrop: 'static'
+ })
+
+ const shownCallback = () => {
+ setTimeout(() => {
+ expect(modal._isShown).toEqual(true)
+ done()
+ }, 10)
+ }
+
+ modalEl.addEventListener('shown.bs.modal', () => {
+ modalEl.click()
+ shownCallback()
+ })
+
+ modalEl.addEventListener('hidden.bs.modal', () => {
+ throw new Error('Should not hide a modal')
+ })
+
+ modal.show()
+ })
+
it('should not adjust the inline body padding when it does not overflow', done => {
fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog" /></div>'
.modal.show & {
transform: $modal-show-transform;
}
+
+ // When trying to close, animate focus to scale
+ .modal.modal-static & {
+ transform: $modal-scale-transform;
+ }
}
.modal-dialog-scrollable {
$modal-fade-transform: translate(0, -50px) !default;
$modal-show-transform: none !default;
$modal-transition: transform .3s ease-out !default;
+$modal-scale-transform: scale(1.02) !default;
// Alerts
</div>
{{< /highlight >}}
+### Static backdrop
+
+When backdrop is set to static, the modal will not close when clicking outside it. Click the button below to try it.
+
+<div id="staticBackdropLive" class="modal fade" data-backdrop="static" tabindex="-1" role="dialog" aria-labelledby="staticBackdropLiveLabel" aria-hidden="true">
+ <div class="modal-dialog" role="document">
+ <div class="modal-content">
+ <div class="modal-header">
+ <h5 class="modal-title" id="staticBackdropLiveLabel">Modal title</h5>
+ <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+ <span aria-hidden="true">×</span>
+ </button>
+ </div>
+ <div class="modal-body">
+ <p>I will not close if you click outside me. Don't even try to press escape key.</p>
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
+ <button type="button" class="btn btn-primary">Understood</button>
+ </div>
+ </div>
+ </div>
+</div>
+
+<div class="bd-example">
+ <button type="button" class="btn btn-primary" data-toggle="modal" data-target="#staticBackdropLive">
+ Launch static backdrop modal
+ </button>
+</div>
+
+{{< highlight html >}}
+<!-- Button trigger modal -->
+<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#staticBackdrop">
+ Launch static backdrop modal
+</button>
+
+<!-- Modal -->
+<div class="modal fade" id="staticBackdrop" data-backdrop="static" tabindex="-1" role="dialog" aria-labelledby="staticBackdropLabel" aria-hidden="true">
+ <div class="modal-dialog" role="document">
+ <div class="modal-content">
+ <div class="modal-header">
+ <h5 class="modal-title" id="staticBackdropLabel">Modal title</h5>
+ <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+ <span aria-hidden="true">×</span>
+ </button>
+ </div>
+ <div class="modal-body">
+ ...
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
+ <button type="button" class="btn btn-primary">Understood</button>
+ </div>
+ </div>
+ </div>
+</div>
+{{< /highlight >}}
+
+
### Scrolling long content
When modals become too long for the user's viewport or device, they scroll independent of the page itself. Try the demo below to see what we mean.
<td>backdrop</td>
<td>boolean or the string <code>'static'</code></td>
<td>true</td>
- <td>Includes a modal-backdrop element. Alternatively, specify <code>static</code> for a backdrop which doesn't close the modal on click.</td>
+ <td>Includes a modal-backdrop element. Alternatively, specify <code>static</code> for a backdrop which doesn't close the modal on click or on escape key press.</td>
</tr>
<tr>
<td>keyboard</td>
<td>hidden.bs.modal</td>
<td>This event is fired when the modal has finished being hidden from the user (will wait for CSS transitions to complete).</td>
</tr>
+ <tr>
+ <td>hidePrevented.bs.modal</td>
+ <td>This event is fired when the modal is shown, its backdrop is <code>static</code> and a click outside the modal or a scape key press is performed.</td>
+ </tr>
</tbody>
</table>