Merge pull request #61 from samthor/domnoderemoved
use DOMNodeRemoved to watch for modal dialog removal from DOM
This commit is contained in:
Коммит
9e896b4322
|
@ -66,7 +66,6 @@ When using the polyfill, the backdrop will be an adjacent element:
|
|||
|
||||
- Modal dialogs have limitations-
|
||||
- They should be a child of `<body>` or have parents without layout (aka, no position `absolute` or `relative` elements)
|
||||
- DOM changes may not correctly clear the top layer, such as when the `<dialog>` is removed from the page
|
||||
- The browser's chrome may not be accessible via the tab key
|
||||
- Stacking can be ruined by playing with z-index
|
||||
- Changes to the CSS top/bottom values while open aren't retained
|
||||
|
|
|
@ -55,12 +55,13 @@
|
|||
dialog.close = this.close.bind(this);
|
||||
|
||||
if ('MutationObserver' in window) {
|
||||
var mo = new MutationObserver(this.maybeHideModal_.bind(this));
|
||||
var mo = new MutationObserver(this.maybeHideModal.bind(this));
|
||||
mo.observe(dialog, { attributes: true, attributeFilter: ['open'] });
|
||||
} else {
|
||||
// TODO: Support for IE9-10.
|
||||
}
|
||||
// TODO: Watch for removal from the DOM.
|
||||
// Note that the DOM is observed inside DialogManager while any dialog
|
||||
// is being displayed as a modal, to catch modal removal from the DOM.
|
||||
|
||||
Object.defineProperty(dialog, 'open', {
|
||||
set: this.setOpen.bind(this),
|
||||
|
@ -83,7 +84,7 @@
|
|||
* a modal dialog may no longer be tenable, e.g., when the dialog is no
|
||||
* longer open or is no longer part of the DOM.
|
||||
*/
|
||||
maybeHideModal_: function() {
|
||||
maybeHideModal: function() {
|
||||
if (!this.openAsModal_) { return; }
|
||||
if (this.dialog_.hasAttribute('open') &&
|
||||
document.body.contains(this.dialog_)) { return; }
|
||||
|
@ -115,7 +116,7 @@
|
|||
this.dialog_.hasAttribute('open') || this.dialog_.setAttribute('open', '');
|
||||
} else {
|
||||
this.dialog_.removeAttribute('open');
|
||||
this.maybeHideModal_(); // nb. redundant with MutationObserver
|
||||
this.maybeHideModal(); // nb. redundant with MutationObserver
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -305,6 +306,7 @@
|
|||
|
||||
this.handleKey_ = this.handleKey_.bind(this);
|
||||
this.handleFocus_ = this.handleFocus_.bind(this);
|
||||
this.handleRemove_ = this.handleRemove_.bind(this);
|
||||
|
||||
this.zIndexLow_ = 100000;
|
||||
this.zIndexHigh_ = 100000 + 150;
|
||||
|
@ -329,6 +331,7 @@
|
|||
document.body.appendChild(this.overlay);
|
||||
document.body.addEventListener('focus', this.handleFocus_, true);
|
||||
document.addEventListener('keydown', this.handleKey_);
|
||||
document.addEventListener('DOMNodeRemoved', this.handleRemove_);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -339,6 +342,7 @@
|
|||
document.body.removeChild(this.overlay);
|
||||
document.body.removeEventListener('focus', this.handleFocus_, true);
|
||||
document.removeEventListener('keydown', this.handleKey_);
|
||||
document.removeEventListener('DOMNodeRemoved', this.handleRemove_);
|
||||
};
|
||||
|
||||
dialogPolyfill.DialogManager.prototype.updateStacking = function() {
|
||||
|
@ -379,6 +383,23 @@
|
|||
}
|
||||
};
|
||||
|
||||
dialogPolyfill.DialogManager.prototype.handleRemove_ = function(event) {
|
||||
if (!/dialog/i.test(event.target.nodeName)) { return; }
|
||||
|
||||
var dialog = /** @type {HTMLDialogElement} */ (event.target);
|
||||
if (!dialog.open) { return; }
|
||||
|
||||
// Find a dialogPolyfillInfo which matches the removed <dialog>.
|
||||
this.pendingDialogStack.some(function(dpi) {
|
||||
if (dpi.dialog == dialog) {
|
||||
// This call will clear the dialogPolyfillInfo on this DialogManager
|
||||
// as a side effect.
|
||||
dpi.maybeHideModal();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {!dialogPolyfillInfo} dpi
|
||||
* @return {boolean} whether the dialog was allowed
|
||||
|
|
23
suite.js
23
suite.js
|
@ -149,6 +149,29 @@ void function() {
|
|||
assert.isTrue(dialog.open, 'can open non-modal outside document');
|
||||
assert.isFalse(document.body.contains(dialog));
|
||||
});
|
||||
test('DOM removal', function(done) {
|
||||
dialog.showModal();
|
||||
assert.isTrue(dialog.open);
|
||||
|
||||
assert.isNotNull(document.querySelector('.backdrop'));
|
||||
|
||||
var parentNode = dialog.parentNode;
|
||||
parentNode.removeChild(dialog);
|
||||
|
||||
// DOMNodeRemoved happens at the end of the frame: this test must be
|
||||
// async to complete successfully.
|
||||
window.setTimeout(function() {
|
||||
assert.isNull(document.querySelector('.backdrop'), 'dialog removal should clear modal');
|
||||
|
||||
assert.isTrue(dialog.open, 'removed dialog should still be open');
|
||||
parentNode.appendChild(dialog);
|
||||
|
||||
assert.isTrue(dialog.open, 'removed dialog should still be open');
|
||||
assert.isNull(document.querySelector('.backdrop'), 're-add dialog should not be modal');
|
||||
|
||||
done();
|
||||
}, 0);
|
||||
});
|
||||
});
|
||||
|
||||
suite('position', function() {
|
||||
|
|
Загрузка…
Ссылка в новой задаче