我使用了一个HTML <dialog> 元素。我希望能够在点击元素外部时关闭对话框。使用“blur”或“focusout”事件无效。
我想要与Material Design对话框相同的功能,在点击对话框外部时关闭它:
我想要与Material Design对话框相同的功能,在点击对话框外部时关闭它:
https://material-components-web.appspot.com/dialog.html
我该如何实现这个目标?
提前致谢。
https://material-components-web.appspot.com/dialog.html
我该如何实现这个目标?
提前致谢。
当以模态模式打开对话框时,在视口的任何位置单击都将被记录为该对话框上的单击。
HTMLDialogElement接口的showModal()方法将对话框显示为模态,位于可能存在的任何其他对话框的顶部。它显示到顶层,伴随着一个::backdrop伪元素。对话框外的交互被阻止,其外部内容被渲染为无效。 来源:HTMLDialogElement.showModal()
解决问题的一种方法是:
You can test this with the code snippet below.
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();
}
要通过单击背景来关闭模态对话框(即使用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>
中,然后用它来检测点击是否在对话框外面,如 这里 所建议的。示例中的填充和边距被调整以确保 <dialog>
边框和 <div>
边框重合。
请注意,可以使用 ::backdrop
在 CSS 中选择模态对话框的“背景”。
对于一个非模态对话框(使用 show 打开),你可以将事件监听器添加到 window
元素而不是 dialog
,例如:
window.addEventListener('click', (event) => {
if (!['my-button', 'my-div'].includes(event.target.id)) {
dialog.close();
}
});
HTMLDialogElement.close()
的用法。如果你不喜欢这种行为,你可以将监听器附加到另一个元素并添加一些逻辑。 - djvgshowModal()
可以让页面上任何位置的点击都聚焦在对话框元素上,这就是为什么添加点击监听器实际上起作用的原因。 - Harald这是一个完整的例子,包含两个对话框元素,一个纯信息性的,另一个包括一个对话框表单。
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://dev59.com/mGUq5IYBdhLWcg3wSOfR#35252091 */
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>
dialog
元素。HTMLDialogElement.prototype.triggerShow = HTMLDialogElement.prototype.showModal;
HTMLDialogElement.prototype.showModal = function() {
this.triggerShow();
this.onclick = event => {
let rect = this.getBoundingClientRect();
if(event.clientY < rect.top || event.clientY > rect.bottom) return this.close();
if(event.clientX < rect.left || event.clientX > rect.right) return this.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');
});
showModal()
,则在背景上单击将被视为对对话框元素的单击。 - 1j01<dialog>
元素 - 它不会像在一个正确的对话框上使用showModal
一样行为。 - mindplay.dk