当对话框以模态模式打开时,视口上任意位置的点击都将被记录为对该对话框的点击。
HTMLDialogElement 接口的 showModal() 方法将对话框显示为模式,位于可能存在的任何其他对话框的顶部。它与 ::backdrop 伪元素一起显示到顶层。 对话外的交互被阻止,对话外的内容呈现惰性。 来源:HTMLDialogElement.showModal()
解决问题的一种方法是:
div
,并使用CSS,确保它覆盖与对话框相同的区域(请注意,浏览器将默认样式应用于对话框,例如填充)div
传播(这样如果用户点击它,对话框就不会关闭)您可以使用下面的代码片段对此进行测试。
const myButton = document.getElementById('myButton');
myButton.addEventListener('click', () => myDialog.showModal());
const myDialog = document.getElementById('myDialog');
myDialog.addEventListener('click', () => myDialog.close());
const myDiv = document.getElementById('myDiv');
myDiv.addEventListener('click', (event) => event.stopPropagation());
#myDialog {
width: 200px;
height: 100px;
padding: 0;
}
#myDiv {
width: 100%;
height: 100%;
padding: 1rem;
}
<button id="myButton">Open dialog</button>
<dialog id="myDialog">
<div id="myDiv">
Click me and I'll stay...
</div>
</dialog>
我是这样做的:
function dialogClickHandler(e) {
if (e.target.tagName !== 'DIALOG') //This prevents issues with forms
return;
const rect = e.target.getBoundingClientRect();
const clickedInDialog = (
rect.top <= e.clientY &&
e.clientY <= rect.top + rect.height &&
rect.left <= e.clientX &&
e.clientX <= rect.left + rect.width
);
if (clickedInDialog === false)
e.target.close();
}
要通过单击背景close一个modal对话框(即用
showModal
打开的对话框),您可以执行以下操作:
const button = document.getElementById('my-button');
const dialog = document.getElementById('my-dialog');
button.addEventListener('click', () => {dialog.showModal();});
// here's the closing part:
dialog.addEventListener('click', (event) => {
if (event.target.id !== 'my-div') {
dialog.close();
}
});
#my-dialog {padding: 0;}
#my-div {padding: 16px;}
<button id="my-button">open dialog</button>
<dialog id="my-dialog">
<div id="my-div">click outside to close</div>
</dialog>
这会将对话框内容放在
<div>
中,然后用于检测点击是否在对话框之外,如 here 所建议的那样。调整示例中的填充和边距以确保 <dialog>
边框和 <div>
边框重合。
::backdrop
在 CSS 中选择模态对话框的“背景”。
对于非模态对话框(使用show打开),您可以将事件侦听器添加到
window
元素而不是dialog
,例如:
window.addEventListener('click', (event) => {
if (!['my-button', 'my-div'].includes(event.target.id)) {
dialog.close();
}
});
在这种情况下我们还需要过滤掉按钮点击,否则点击“打开对话框”按钮后对话框立即关闭
好吧,用你说话的方式来编码。 如果您单击的元素不是所需的对话框,请关闭该对话框。 这是一个例子:
<div id="content">
<div id="dialog" class="dialogComponent">
<div id="foo" class="dialogComponent">
test 123 123 123 123
<input class="dialogComponent class2" type="text">
</div>
<button class="dialogComponent">Submit</button>
</div>
</div>
#content { width: 100%; height: 333px; background-color: black;}
#dialog { margin: 33px; background-color: blue; }
$('#content').click(function(e) {
if (!e.target.classList.contains("dialogComponent"))
alert('Closing Dialog');
});
这里是一个包含两个对话框元素的完整示例,一个是纯粹的信息,另一个包括一个 dialog 表单。
const initializeDialog = function(dialogElement) {
// enhance opened standard HTML dialog element by closing it when clicking outside of it
dialogElement.addEventListener('click', function(event) {
const eventTarget = event.target;
if (dialogElement === eventTarget) {
console.log("click on dialog element's content, padding, border, or margin");
const dialogElementRect = dialogElement.getBoundingClientRect();
console.log("dialogElementRect.width", dialogElementRect.width);
console.log("dialogElementRect.height", dialogElementRect.height);
console.log("dialogElementRect.top", dialogElementRect.top);
console.log("dialogElementRect.left", dialogElementRect.left);
console.log("event.offsetX", event.offsetX);
console.log("event.clientX", event.clientX);
console.log("event.offsetY", event.offsetY);
console.log("event.clientY", event.clientY);
if (
(dialogElementRect.top > event.clientY) ||
(event.clientY > (dialogElementRect.top + dialogElementRect.height)) ||
(dialogElementRect.left > event.clientX) ||
(event.clientX > (dialogElementRect.left + dialogElementRect.width))
) {
console.log("click on dialog element's margin. closing dialog element");
dialogElement.close();
}
else {
console.log("click on dialog element's content, padding, or border");
}
}
else {
console.log("click on an element WITHIN dialog element");
}
});
const maybeDialogFormElement = dialogElement.querySelector('form[method="dialog"]');
if (! maybeDialogFormElement) {
// this dialog element does NOT contain a "<form method="dialog">".
// Hence, any contained buttons intended for closing the dialog will
// NOT be automatically set up for closing the dialog
// (see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog#usage_notes ).
// Therefore, programmatically set up close buttons
const closeButtons = dialogElement.querySelectorAll('button[data-action-close], button[data-action-cancel]');
closeButtons.forEach(closeButton => {
closeButton.addEventListener('click', () => dialogElement.close() );
});
}
return dialogElement;
};
const initializeFormDialog = function(formDialog, formCloseHandler) {
const submitButton = formDialog.querySelector('button[type="submit"]');
const inputElement = formDialog.querySelector('input');
formDialog.originalShowModal = formDialog.showModal;
formDialog.showModal = function() {
// populate input element with initial or latest submit value
inputElement.value = submitButton.value;
formDialog.dataset.initialInputElementValue = inputElement.value;
formDialog.originalShowModal();
}
// allow confirm-input-by-pressing-Enter-within-input-element
inputElement.addEventListener('keydown', event => {
if (event.key === 'Enter') {
//prevent default action, which in dialog-form case would effectively cancel, not confirm the dialog
event.preventDefault();
submitButton.click();
}
});
submitButton.addEventListener('click', () => {
submitButton.value = inputElement.value;
// add dialog-was-confirmed marker
formDialog.dataset.confirmed = "true";
});
formDialog.addEventListener('close', event => {
if (formCloseHandler) {
const returnValue = formDialog.returnValue;
const dialogWasConfirmed = (formDialog.dataset.confirmed === "true");
let inputElementValueHasChanged;
if (dialogWasConfirmed) {
inputElementValueHasChanged = (returnValue === formDialog.dataset.initialInputElementValue) ? false : true;
}
else {
inputElementValueHasChanged = false;
}
formCloseHandler(returnValue, dialogWasConfirmed, inputElementValueHasChanged);
}
// remove dialog-was-confirmed marker
delete formDialog.dataset.confirmed;
});
};
const myFormDialogCloseHandler = function(returnValue, dialogWasConfirmed, inputElementValueHasChanged) {
const resultDebugOutput = document.getElementById('output-result');
const resultDebugEntryString = `<pre>dialog confirmed? ${dialogWasConfirmed}
input value changed? ${inputElementValueHasChanged}
returnValue: "${returnValue}"</pre>`;
resultDebugOutput.insertAdjacentHTML('beforeend', resultDebugEntryString);
};
const informationalDialog = document.getElementById('dialog-informational');
initializeDialog(informationalDialog);
const showDialogInformationalButton = document.getElementById('button-show-dialog-informational');
showDialogInformationalButton.addEventListener('click', () => informationalDialog.showModal());
const formDialog = document.getElementById('dialog-form');
initializeDialog(formDialog);
initializeFormDialog(formDialog, myFormDialogCloseHandler);
const showDialogFormButton = document.getElementById('button-show-dialog-form');
showDialogFormButton.addEventListener('click', () => {
formDialog.showModal();
});
dialog {
/* for demonstrational purposes, provide different styles for content, padding, and border */
background-color: LightSkyBlue;
border: 2rem solid black;
/* give padding a color different from content; see https://stackoverflow.com/a/35252091/923560 */
padding: 1rem;
box-shadow: inset 0 0 0 1rem LightGreen;
}
dialog header {
display: flex;
justify-content: space-between;
gap: 1rem;
align-items: flex-start;
}
dialog header button[data-action-close]::before,
dialog header button[data-action-cancel]::before {
content: "✕";
}
dialog footer {
display: flex;
justify-content: flex-end;
gap: 1rem;
}
<button id="button-show-dialog-informational" type="button">Show informational dialog</button>
<button id="button-show-dialog-form" type="button">Show dialog with form</button>
<dialog id="dialog-informational">
<header>
<strong>Informational dialog header</strong>
<button aria-labelledby="dialog-close" data-action-close="true"></button>
</header>
<div>
<p>This is the dialog content.</p>
</div>
<footer>
<button id="dialog-close" data-action-close="true">Close dialog</button>
</footer>
</dialog>
<dialog id="dialog-form">
<form method="dialog">
<header>
<strong>Dialog with form</strong>
<button aria-labelledby="dialog-form-cancel" data-action-cancel="true" value="cancel-header"></button>
</header>
<div>
<p>This is the dialog content.</p>
<label for="free-text-input">Text input</label>
<input type="text" id="free-text-input" name="free-text-input" />
</div>
<footer>
<button id="dialog-form-cancel" value="cancel-footer">Cancel</button>
<button type="submit" id="dialog-form-confirm" value="initial value">Confirm</button>
</footer>
</form>
</dialog>
<div id="output-result"></div>