demo · v136
Unsaved-changes confirmation
The motivating use case from the Open UI explainer: a real editor dialog whose backdrop click, Esc key and Cancel button all go through the same gate. request-close fires a cancel event we use to detect dirty state and bounce the user into a nested confirmation dialog — no per-trigger glue code.
HTMLDialogElement.prototype.requestClose…Form is clean.
event log
the code
// One handler. ESC, backdrop click, AND the <button command="request-close">
// all fire "cancel" on the dialog before close. We can gate ALL of them with
// one event listener — no per-button onclick wiring.
editor.addEventListener("cancel", (e) => {
if (formIsDirty(editor.querySelector("form"))) {
e.preventDefault(); // keep editor open
confirm.showModal(); // ask "discard?" — sub-dialog handles the real close
}
});
discardBtn.addEventListener("click", () => {
confirm.close();
editor.close(); // bypasses the cancel gate
});
why this angle
The Open UI explainer for invoker commands and the WHATWG discussion thread call out unsaved-form-confirmation as the motivating reason request-close needed to be its own command (rather than just close). Without it, you'd have to wire the cancel gate three different times: once on the Cancel button, once on backdrop click, once on Esc. With it, the dialog has one cancel entry point and the gate lives in one place. This concept builds the actual editor; the sibling concept shows the raw event mechanics on a toy dialog.