Sie kennen doch die JavaScript-Dialogfelder zum Melden, Bestätigen und Abfragen von Benutzeraktionen? Sagen wir, Sie möchten JavaScript-Dialogfelder durch das neue HTML-Dialogfeld-Element ersetzen.
Ich werde es erklären.
Ich habe kürzlich an einem Projekt gearbeitet, bei dem viele API-Aufrufe und Benutzerfeedbacks über JavaScript-Dialogfelder gesammelt wurden. Während ich auf die Implementierung der <Modal /> Komponente durch einen anderen Entwickler wartete, verwendete ich alert(), confirm() und prompt() in meinem Code. Zum Beispiel
const deleteLocation = confirm('Delete location');
if (deleteLocation) {
alert('Location deleted');
}
Dann traf es mich: Sie erhalten viele modalbezogene Funktionen kostenlos mit alert(), confirm() und prompt(), die oft übersehen werden
- Es ist ein echtes Modal. Das heißt, es wird immer oben in der Stapelreihenfolge liegen – sogar über diesem
<div>mitz-index: 99999;. - Es ist mit der Tastatur zugänglich. Drücken Sie
Enterzum Akzeptieren undEscapezum Abbrechen. - Es ist bildschirmleserfreundlich. Es verschiebt den Fokus und ermöglicht, dass der modale Inhalt laut vorgelesen wird.
- Es fängt den Fokus ein. Das Drücken von
Taberreicht keine fokussierbaren Elemente auf der Hauptseite, aber in Firefox und Safari bewegt es den Fokus tatsächlich zur Browser-Benutzeroberfläche. Seltsam ist jedoch, dass man den Fokus in keinem Browser mit derTab-Taste auf die "Akzeptieren" oder "Abbrechen"-Schaltflächen bewegen kann. - Es unterstützt Benutzereinstellungen. Wir erhalten automatisch eine Unterstützung für Hell- und Dunkelmodus direkt out of the box.
- Es pausiert die Codeausführung. Außerdem wartet es auf Benutzereingaben.
Diese drei JavaScript-Methoden funktionieren in 99 % der Fälle, wenn ich eine dieser Funktionalitäten benötige. Warum also verwende ich – oder wirklich irgendein anderer Webentwickler – sie nicht? Wahrscheinlich, weil sie wie Systemfehler aussehen, die nicht gestylt werden können. Ein weiterer wichtiger Aspekt: Es gab Bestrebungen zu ihrer Stilllegung. Zuerst die Entfernung aus Cross-Domain-Iframes und Gerüchten zufolge aus der gesamten Webplattform, obwohl es auch so aussieht, als wären die Pläne dafür auf Eis gelegt.
Mit diesem wichtigen Aspekt im Hinterkopf, welche Alternativen zu alert(), confirm() und prompt() haben wir, um sie zu ersetzen? Möglicherweise haben Sie bereits vom <dialog> HTML-Element gehört, und das ist es, was ich in diesem Artikel untersuchen möchte, indem ich es zusammen mit einer JavaScript Klasse verwende.
Es ist unmöglich, JavaScript-Dialogfelder vollständig durch identische Funktionalität zu ersetzen, aber wenn wir die Methode showModal() von <dialog> mit einem Promise kombinieren, das entweder resolve (akzeptieren) oder reject (abbrechen) kann – dann haben wir etwas fast so Gutes. Verdammt, während wir dabei sind, fügen wir dem HTML-Dialogfeld einen Ton hinzu – genau wie echte Systemdialogfelder!
Wenn Sie die Demo sofort sehen möchten, finden Sie sie hier.
Eine Dialogklasse
Zuerst benötigen wir eine grundlegende JavaScript Klasse mit einem settings Objekt, das mit den Standardeinstellungen zusammengeführt wird. Diese Einstellungen werden für alle Dialogfelder verwendet, es sei denn, Sie überschreiben sie bei deren Aufruf (aber dazu später mehr).
export default class Dialog {
constructor(settings = {}) {
this.settings = Object.assign(
{
/* DEFAULT SETTINGS - see description below */
},
settings
)
this.init()
}
Die Einstellungen sind
accept: Dies ist die Beschriftung der Schaltfläche "Akzeptieren".bodyClass: Dies ist eine CSS-Klasse, die dem<body>Element hinzugefügt wird, wenn das Dialogfeldopenist und das<dialog>Element vom Browser nicht unterstützt wird.cancel: Dies ist die Beschriftung der Schaltfläche "Abbrechen".dialogClass: Dies ist eine benutzerdefinierte CSS-Klasse, die dem<dialog>Element hinzugefügt wird.message: Dies ist der Inhalt innerhalb des<dialog>.soundAccept: Dies ist die URL der Sounddatei, die wir abspielen, wenn der Benutzer die Schaltfläche "Akzeptieren" drückt.soundOpen: Dies ist die URL der Sounddatei, die wir abspielen, wenn der Benutzer den Dialog öffnet.template: Dies ist eine optionale, kleine HTML-Vorlage, die in das<dialog>eingefügt wird.
Die anfängliche Vorlage zum Ersetzen von JavaScript-Dialogen
In der init Methode fügen wir eine Hilfsfunktion hinzu, um die Unterstützung für das HTML-Dialogfeld in Browsern zu erkennen, und richten das grundlegende HTML ein
init() {
// Testing for <dialog> support
this.dialogSupported = typeof HTMLDialogElement === 'function'
this.dialog = document.createElement('dialog')
this.dialog.dataset.component = this.dialogSupported ? 'dialog' : 'no-dialog'
this.dialog.role = 'dialog'
// HTML template
this.dialog.innerHTML = `
<form method="dialog" data-ref="form">
<fieldset data-ref="fieldset" role="document">
<legend data-ref="message" id="${(Math.round(Date.now())).toString(36)}">
</legend>
<div data-ref="template"></div>
</fieldset>
<menu>
<button data-ref="cancel" value="cancel"></button>
<button data-ref="accept" value="default"></button>
</menu>
<audio data-ref="soundAccept"></audio>
<audio data-ref="soundOpen"></audio>
</form>`
document.body.appendChild(this.dialog)
// ...
}
Prüfung auf Unterstützung
Der Weg für die Browser zur Unterstützung von <dialog> war lang. Safari hat es ziemlich kürzlich übernommen. Firefox noch kürzlicher, wenn auch nicht den Teil <form method="dialog">. Also müssen wir type="button" zu den "Akzeptieren" und "Abbrechen" Schaltflächen hinzufügen, die wir nachahmen. Andernfalls werden sie das Formular POSTen und einen Seiten-Refresh verursachen, was wir vermeiden wollen.
<button${this.dialogSupported ? '' : ` type="button"`}...></button>
DOM-Knotenreferenzen
Haben Sie all die data-ref-Attribute bemerkt? Wir werden diese verwenden, um Referenzen zu den DOM-Knoten zu erhalten
this.elements = {}
this.dialog.querySelectorAll('[data-ref]').forEach(el => this.elements[el.dataset.ref] = el)
Bisher ist this.elements.accept eine Referenz auf die Schaltfläche "Akzeptieren", und this.elements.cancel bezieht sich auf die Schaltfläche "Abbrechen".
Schaltflächenattribute
Für Bildschirmleser benötigen wir ein aria-labelledby Attribut, das auf die ID des Tags verweist, der den Dialog beschreibt – das ist das <legend> Tag und es enthält die message.
this.dialog.setAttribute('aria-labelledby', this.elements.message.id)
Diese id? Sie ist eine eindeutige Referenz auf diesen Teil des <legend> Elements
Die "Abbrechen"-Schaltfläche
Gute Nachrichten! Das HTML-Dialogfeld-Element hat eine eingebaute Methode cancel(), die es einfacher macht, JavaScript-Dialogfelder zu ersetzen, die die Methode confirm() aufrufen. Lassen Sie uns dieses Ereignis auslösen, wenn wir auf die Schaltfläche "Abbrechen" klicken
this.elements.cancel.addEventListener('click', () => {
this.dialog.dispatchEvent(new Event('cancel'))
})
Das ist das Framework für unser <dialog>, um alert(), confirm() und prompt() zu ersetzen.
Polyfilling für nicht unterstützte Browser
Wir müssen das HTML-Dialogfeld für Browser ausblenden, die es nicht unterstützen. Dazu verpacken wir die Logik zum Anzeigen und Ausblenden des Dialogfelds in einer neuen Methode, toggle()
toggle(open = false) {
if (this.dialogSupported && open) this.dialog.showModal()
if (!this.dialogSupported) {
document.body.classList.toggle(this.settings.bodyClass, open)
this.dialog.hidden = !open
/* If a `target` exists, set focus on it when closing */
if (this.elements.target && !open) {
this.elements.target.focus()
}
}
}
/* Then call it at the end of `init`: */
this.toggle()
Tastaturnavigation
Als Nächstes implementieren wir eine Möglichkeit, den Fokus einzufangen, damit der Benutzer zwischen den Schaltflächen im Dialogfeld tabulieren kann, ohne es versehentlich zu verlassen. Dafür gibt es viele Möglichkeiten. Ich mag den CSS-Weg, aber leider ist er nicht zuverlässig. Stattdessen greifen wir alle fokussierbaren Elemente aus dem Dialogfeld als NodeList ab und speichern sie in this.focusable
getFocusable() {
return [...this.dialog.querySelectorAll('button,[href],select,textarea,input:not([type="hidden"]),[tabindex]:not([tabindex="-1"])')]
}
Als Nächstes fügen wir einen keydown-Ereignis-Listener hinzu, der die gesamte Logik für die Tastaturnavigation abwickelt
this.dialog.addEventListener('keydown', e => {
if (e.key === 'Enter') {
if (!this.dialogSupported) e.preventDefault()
this.elements.accept.dispatchEvent(new Event('click'))
}
if (e.key === 'Escape') this.dialog.dispatchEvent(new Event('cancel'))
if (e.key === 'Tab') {
e.preventDefault()
const len = this.focusable.length - 1;
let index = this.focusable.indexOf(e.target);
index = e.shiftKey ? index-1 : index+1;
if (index < 0) index = len;
if (index > len) index = 0;
this.focusable[index].focus();
}
})
Für Enter müssen wir verhindern, dass das <form> in Browsern gesendet wird, in denen das <dialog> Element nicht unterstützt wird. Escape löst ein cancel-Ereignis aus. Das Drücken der Tab-Taste findet das aktuelle Element in der Knotenliste der fokussierbaren Elemente, this.focusable, und setzt den Fokus auf das nächste Element (oder das vorherige, wenn Sie gleichzeitig die Shift-Taste gedrückt halten).
Anzeigen des <dialog>
Nun lassen Sie uns den Dialog anzeigen! Dazu benötigen wir eine kleine Methode, die ein optionales settings Objekt mit den Standardwerten zusammenführt. In diesem Objekt – genau wie im Standard settings Objekt – können wir Einstellungen für einen bestimmten Dialog hinzufügen oder ändern.
open(settings = {}) {
const dialog = Object.assign({}, this.settings, settings)
this.dialog.className = dialog.dialogClass || ''
/* set innerText of the elements */
this.elements.accept.innerText = dialog.accept
this.elements.cancel.innerText = dialog.cancel
this.elements.cancel.hidden = dialog.cancel === ''
this.elements.message.innerText = dialog.message
/* If sounds exists, update `src` */
this.elements.soundAccept.src = dialog.soundAccept || ''
this.elements.soundOpen.src = dialog.soundOpen || ''
/* A target can be added (from the element invoking the dialog */
this.elements.target = dialog.target || ''
/* Optional HTML for custom dialogs */
this.elements.template.innerHTML = dialog.template || ''
/* Grab focusable elements */
this.focusable = this.getFocusable()
this.hasFormData = this.elements.fieldset.elements.length > 0
if (dialog.soundOpen) {
this.elements.soundOpen.play()
}
this.toggle(true)
if (this.hasFormData) {
/* If form elements exist, focus on that first */
this.focusable[0].focus()
this.focusable[0].select()
}
else {
this.elements.accept.focus()
}
}
Puh! Das war viel Code. Jetzt können wir das <dialog> Element in allen Browsern anzeigen. Aber wir müssen immer noch die Funktionalität nachahmen, die nach der Ausführung auf eine Benutzereingabe wartet, wie die nativen Methoden alert(), confirm() und prompt(). Dafür brauchen wir ein Promise und eine neue Methode, die ich waitForUser() nenne.
waitForUser() {
return new Promise(resolve => {
this.dialog.addEventListener('cancel', () => {
this.toggle()
resolve(false)
}, { once: true })
this.elements.accept.addEventListener('click', () => {
let value = this.hasFormData ?
this.collectFormData(new FormData(this.elements.form)) : true;
if (this.elements.soundAccept.src) this.elements.soundAccept.play()
this.toggle()
resolve(value)
}, { once: true })
})
}
Diese Methode gibt ein Promise zurück. Darin fügen wir Event-Listener für "cancel" und "accept" hinzu, die entweder false (cancel) oder true (accept) auflösen. Wenn formData vorhanden ist (für benutzerdefinierte Dialoge oder prompt), werden diese mit einer Hilfsmethode gesammelt und dann in einem Objekt zurückgegeben
collectFormData(formData) {
const object = {};
formData.forEach((value, key) => {
if (!Reflect.has(object, key)) {
object[key] = value
return
}
if (!Array.isArray(object[key])) {
object[key] = [object[key]]
}
object[key].push(value)
})
return object
}
Wir können die Event-Listener sofort entfernen, indem wir { once: true } verwenden.
Um es einfach zu halten, verwende ich nicht reject(), sondern löse einfach false auf.
Ausblenden des <dialog>
Vorhin haben wir Event-Listener für das eingebaute cancel-Ereignis hinzugefügt. Wir rufen dieses Ereignis auf, wenn der Benutzer auf die Schaltfläche "Abbrechen" klickt oder die Escape-Taste drückt. Das cancel-Ereignis entfernt das Attribut open vom <dialog> und blendet es somit aus.
Wo :focus?
In unserer open() Methode setzen wir den Fokus entweder auf das erste fokussierbare Formularfeld oder auf die Schaltfläche "Akzeptieren".
if (this.hasFormData) {
this.focusable[0].focus()
this.focusable[0].select()
}
else {
this.elements.accept.focus()
}
Aber ist das richtig? Im W3 "Modal Dialog" Beispiel ist das tatsächlich der Fall. In Scott Oharas Beispiel hingegen liegt der Fokus auf dem Dialogfeld selbst – was Sinn macht, wenn der Bildschirmleser den Text vorlesen soll, den wir zuvor im aria-labelledby Attribut definiert haben. Ich bin mir nicht sicher, was richtig oder am besten ist, aber wenn wir Scotts Methode verwenden wollen, müssen wir in unserer init Methode ein tabindex="-1" zum <dialog> hinzufügen.
this.dialog.tabIndex = -1
Dann, in der open() Methode, ersetzen wir den Fokus-Code durch diesen
this.dialog.focus()
Sie können das activeElement (das Element, das den Fokus hat) jederzeit in den DevTools überprüfen, indem Sie auf das "Auge"-Symbol klicken und document.activeElement in der Konsole eingeben. Versuchen Sie, durch die Elemente zu tabulieren, um zu sehen, wie es sich aktualisiert

Hinzufügen von alert, confirm und prompt
Wir sind endlich bereit, alert(), confirm() und prompt() zu unserer Dialog-Klasse hinzuzufügen. Dies werden kleine Hilfsmethoden sein, die JavaScript-Dialogfelder und die ursprüngliche Syntax dieser Methoden ersetzen. Alle rufen die zuvor erstellte open()-Methode auf, jedoch mit einem settings-Objekt, das der Art und Weise entspricht, wie wir die ursprünglichen Methoden auslösen.
Vergleichen wir dies mit der ursprünglichen Syntax.
alert() wird normalerweise so ausgelöst
window.alert(message);
In unserem Dialog fügen wir eine alert()-Methode hinzu, die dies nachahmt
/* dialog.alert() */
alert(message, config = { target: event.target }) {
const settings = Object.assign({}, config, { cancel: '', message, template: '' })
this.open(settings)
return this.waitForUser()
}
Wir setzen cancel und template auf leere Strings, damit diese – auch wenn wir zuvor Standardwerte gesetzt hätten – nicht ausgeblendet werden und nur message und accept angezeigt werden.
confirm() wird normalerweise so ausgelöst
window.confirm(message);
In unserer Version erstellen wir, ähnlich wie bei alert(), eine benutzerdefinierte Methode, die die Elemente message, cancel und accept anzeigt
/* dialog.confirm() */
confirm(message, config = { target: event.target }) {
const settings = Object.assign({}, config, { message, template: '' })
this.open(settings)
return this.waitForUser()
}
prompt() wird normalerweise so ausgelöst
window.prompt(message, default);
Hier müssen wir eine template mit einem <input> hinzufügen, das wir in ein <label> einbetten
/* dialog.prompt() */
prompt(message, value, config = { target: event.target }) {
const template = `
<label aria-label="${message}">
<input name="prompt" value="${value}">
</label>`
const settings = Object.assign({}, config, { message, template })
this.open(settings)
return this.waitForUser()
}
{ target: event.target } ist eine Referenz auf das DOM-Element, das die Methode aufruft. Wir verwenden dies, um den Fokus wieder auf dieses Element zu setzen, wenn wir den <dialog> schließen, und geben dem Benutzer die Kontrolle zurück, wo er sich vor dem Auslösen des Dialogfelds befand.
Wir sollten das testen
Es ist an der Zeit, zu testen und sicherzustellen, dass alles wie erwartet funktioniert. Erstellen wir eine neue HTML-Datei, importieren wir die Klasse und erstellen eine Instanz
<script type="module">
import Dialog from './dialog.js';
const dialog = new Dialog();
</script>
Probieren Sie die folgenden Anwendungsfälle nacheinander aus!
/* alert */
dialog.alert('Please refresh your browser')
/* or */
dialog.alert('Please refresh your browser').then((res) => { console.log(res) })
/* confirm */
dialog.confirm('Do you want to continue?').then((res) => { console.log(res) })
/* prompt */
dialog.prompt('The meaning of life?', 42).then((res) => { console.log(res) })
Beobachten Sie dann die Konsole, während Sie auf "Akzeptieren" oder "Abbrechen" klicken. Versuchen Sie es erneut, während Sie stattdessen die Tasten Escape oder Enter drücken.
Async/Await
Wir können auch den async/await-Weg dafür nutzen. Wir ersetzen JavaScript-Dialogfelder noch weiter, indem wir die ursprüngliche Syntax nachahmen, aber es erfordert, dass die umgebende Funktion async ist, während der Code darin das Schlüsselwort await erfordert.
document.getElementById('promptButton').addEventListener('click', async (e) => {
const value = await dialog.prompt('The meaning of life?', 42);
console.log(value);
});
Browserübergreifendes Styling
Wir haben jetzt ein voll funktionsfähiges, browser- und bildschirmleserfreundliches HTML-Dialogfeld, das JavaScript-Dialogfelder ersetzt! Wir haben viel behandelt. Aber das Styling könnte viel Liebe gebrauchen. Lassen Sie uns die vorhandenen data-component und data-ref Attribute nutzen, um browserübergreifendes Styling hinzuzufügen – keine Notwendigkeit für zusätzliche Klassen oder andere Attribute!
Wir werden die CSS-Pseudo-Klasse :where verwenden, um unsere Standardstile von der Spezifität freizuhalten.
:where([data-component*="dialog"] *) {
box-sizing: border-box;
outline-color: var(--dlg-outline-c, hsl(218, 79.19%, 35%))
}
:where([data-component*="dialog"]) {
--dlg-gap: 1em;
background: var(--dlg-bg, #fff);
border: var(--dlg-b, 0);
border-radius: var(--dlg-bdrs, 0.25em);
box-shadow: var(--dlg-bxsh, 0px 25px 50px -12px rgba(0, 0, 0, 0.25));
font-family:var(--dlg-ff, ui-sansserif, system-ui, sans-serif);
min-inline-size: var(--dlg-mis, auto);
padding: var(--dlg-p, var(--dlg-gap));
width: var(--dlg-w, fit-content);
}
:where([data-component="no-dialog"]:not([hidden])) {
display: block;
inset-block-start: var(--dlg-gap);
inset-inline-start: 50%;
position: fixed;
transform: translateX(-50%);
}
:where([data-component*="dialog"] menu) {
display: flex;
gap: calc(var(--dlg-gap) / 2);
justify-content: var(--dlg-menu-jc, flex-end);
margin: 0;
padding: 0;
}
:where([data-component*="dialog"] menu button) {
background-color: var(--dlg-button-bgc);
border: 0;
border-radius: var(--dlg-bdrs, 0.25em);
color: var(--dlg-button-c);
font-size: var(--dlg-button-fz, 0.8em);
padding: var(--dlg-button-p, 0.65em 1.5em);
}
:where([data-component*="dialog"] [data-ref="accept"]) {
--dlg-button-bgc: var(--dlg-accept-bgc, hsl(218, 79.19%, 46.08%));
--dlg-button-c: var(--dlg-accept-c, #fff);
}
:where([data-component*="dialog"] [data-ref="cancel"]) {
--dlg-button-bgc: var(--dlg-cancel-bgc, transparent);
--dlg-button-c: var(--dlg-cancel-c, inherit);
}
:where([data-component*="dialog"] [data-ref="fieldset"]) {
border: 0;
margin: unset;
padding: unset;
}
:where([data-component*="dialog"] [data-ref="message"]) {
font-size: var(--dlg-message-fz, 1.25em);
margin-block-end: var(--dlg-gap);
}
:where([data-component*="dialog"] [data-ref="template"]:not(:empty)) {
margin-block-end: var(--dlg-gap);
width: 100%;
}
Sie können diese natürlich nach Belieben gestalten. Hier ist, was der obige CSS-Code Ihnen liefert

alert()
confirm()
prompt()Um diese Stile zu überschreiben und Ihre eigenen zu verwenden, fügen Sie eine Klasse in dialogClass hinzu,
dialogClass: 'custom'
…fügen Sie dann die Klasse in CSS hinzu und aktualisieren Sie die Werte der CSS-Custom-Properties.
.custom {
--dlg-accept-bgc: hsl(159, 65%, 75%);
--dlg-accept-c: #000;
/* etc. */
}
Ein Beispiel für einen benutzerdefinierten Dialog
Was ist, wenn die Standardmethoden alert(), confirm() und prompt(), die wir nachahmen, für Ihren spezifischen Anwendungsfall nicht ausreichen? Wir können tatsächlich noch mehr tun, um das <dialog> flexibler zu gestalten und mehr abzudecken als die Inhalte, Schaltflächen und Funktionalitäten, die wir bisher behandelt haben – und es ist nicht viel mehr Arbeit.
Vorhin habe ich die Idee angedeutet, dem Dialog einen Ton hinzuzufügen. Tun wir das.
Sie können die template-Eigenschaft des settings-Objekts verwenden, um mehr HTML einzufügen. Hier ist ein benutzerdefiniertes Beispiel, aufgerufen von einem <button> mit id="btnCustom", das einen lustigen kleinen Ton aus einer MP3-Datei auslöst.
document.getElementById('btnCustom').addEventListener('click', (e) => {
dialog.open({
accept: 'Sign in',
dialogClass: 'custom',
message: 'Please enter your credentials',
soundAccept: 'https://assets.yourdomain.com/accept.mp3',
soundOpen: 'https://assets.yourdomain.com/open.mp3',
target: e.target,
template: `
<label>Username<input type="text" name="username" value="admin"></label>
<label>Password<input type="password" name="password" value="password"></label>`
})
dialog.waitForUser().then((res) => { console.log(res) })
});
Live-Demo
Hier ist ein Pen mit allem, was wir gebaut haben! Öffnen Sie die Konsole, klicken Sie auf die Schaltflächen und experimentieren Sie mit den Dialogfeldern, klicken Sie auf die Schaltflächen und verwenden Sie die Tastatur zum Akzeptieren und Abbrechen.
Was denken Sie? Ist dies eine gute Methode, um JavaScript-Dialogfelder durch das neuere HTML-Dialogfeld-Element zu ersetzen? Oder haben Sie es auf andere Weise versucht? Lassen Sie es mich in den Kommentaren wissen!
Ich würde sagen, obwohl es großartig aussieht, ist das eine Menge Arbeit für das, was
alert("Hallo!");allein tut.Aber mit diesem Mini-Framework können Sie viel mehr als nur grundlegende Modals machen.
Nicht überraschend eigentlich – die meisten Polyfills sind so. Aber ich denke, der Größenkompromiss macht Sinn, wenn
alertbereits intensiv auf einer Website verwendet wird.Die Leute, die im Internet surfen, hassen jedoch Alerts! Ich stoße im Jahr vielleicht auf 2 bis 3 Alerts insgesamt, wenn ich im Internet surfe. Und die Tatsache, dass es die Browser-Benutzeroberfläche verwendet, irritiert mich und könnte Benutzer verwirren.
Ich denke, es ist am besten, die Verwendung von alert() auf Produktionswebsites oder kundenorientierten Websites zu vermeiden.
Wahr, Alerts können schrecklich sein! In einer Webanwendung sind sie jedoch Teil des natürlichen UI-Flusses.
Toller Artikel! Ich habe einen ähnlichen Ansatz mit Vue.js verwendet, vielleicht interessiert jemanden https://danielkelly.io/blog/renderless-vue-dialog
Habe letzten Sommer einen ersten Ansatz dafür gebloggt (unterschiedliche Implementierung, aber doch).
https://dfkaye.com/posts/2021/08/10/alert-dialog-generator/
Ich mache mir Sorgen, dass böswilliger Code diese Idee nutzen könnte, um Leute zu verwirren und sie denken zu lassen, sie würden einen Dialog abbrechen, und stattdessen einen Dialog akzeptieren.
Die aktuelle Methode kann nicht gefälscht werden.
Das letzte Beispiel für einen benutzerdefinierten Dialog bereitet mir Angst wegen Websites, die ihn als visuell aufgeblähte Methode zur Anzeige von Werbung, einschließlich Tönen, nutzen.
Ja, absolut einverstanden! Aber stellen Sie es sich in einer Webanwendung vor, mit einem "Mülleimer"-Sound, wenn Sie etwas löschen.
Ich bin auch ein großer Fan der nativen Dialogfunktionen des Browsers (
alert(),confirm(),prompt()).Für mein Projekt wollte ich die Modal-Komponente von Bootstrap verwenden, aber sie über einen einfachen Funktionsaufruf nutzen, ohne Zustände definieren und verwalten zu müssen.
Ich habe es mit einem benutzerdefinierten React-Hook realisiert: https://bruegmann.github.io/blue-react/v8/component/ModalProvider
Auch ein cooler Hack, wenn Sie JavaScript vermeiden wollen: Verwenden Sie
<detail>und stylen Sie es mit CSS, damit es wie ein Dialog aussieht: https://codepen.io/lgkonline/pen/MWEmmYvDanke für den Artikel! Ich würde den Leuten empfehlen, sich zuerst anzusehen: https://a11y-dialog.netlify.app/advanced/dialog-element/ (und den Artikel, auf den er verlinkt) um einige Fallstricke mit nativem Dialog zu sehen. Nämlich kann man nicht auf den Overlay klicken, um ihn zu schließen, der alertdialog-Typ deaktiviert Escape nicht, und man kann ihn nur zuverlässig programmatisch öffnen.
Gute Arbeit. Ich habe mir den Prompt-Demo-Fall angesehen. Wenn man vom Eingabefeld weg tabuliert, gelangt man zur Option "Abbrechen". Wie könnte man am besten vom Eingabefeld zur Schaltfläche "OK" tabulieren?
Die Template-Option deckt meines Erachtens nicht das HTML-Layout der Buttons ab. Daher kann ich die Reihenfolge der Button-Deklaration nicht manipulieren oder tabindex festlegen.
Ich denke, eine verwandte Frage ist, wie man den Standard der Windows-Plattform verwendet (OK links und Abbrechen rechts).
Danke Mads für diesen Artikel.
Ein Verbesserungsvorschlag wäre, das Scrollen der Seite zu verhindern, wenn der Dialog geöffnet ist.
Die Dialoge sollten auch verschiebbar sein, damit ich sie bewegen kann, wenn sie Informationen verdecken, die ich für meine Entscheidung benötige.
Verlassen Sie sich niemals auf native alert, prompt oder confirm, da Browser (zumindest Firefox) den Benutzern die Option bieten, diese zu blockieren (nach dem zweiten Mal, wenn Sie sie verwenden), und ich weiß nicht, ob Sie überhaupt erkennen können, dass die Benutzer Ihren Dialog nicht sehen werden.
Bezüglich des nativen Dialogelements seien Sie sich bewusst, dass es Barrierefreiheitsprobleme gibt: https://www.scottohara.me/blog/2019/03/05/open-dialog.html
VIELEN DANK an ALLE für eure Kommentare und Vorschläge!
Ich hoffe aufrichtig, dass einige Entwickler den Pen forken, etwas Neues, Cooles oder Besseres daraus machen und es hier in den Kommentaren teilen!
Absolut einverstanden, es ist besser, ein HTML-Alert anzuzeigen als die nativen Alerts. Ich verwende diese Webkomponente, die gut mit jedem Framework funktioniert, keine Abhängigkeiten hat und sehr leicht und vollständig personalisierbar ist.
https://cuppajs.cloudbit.co/#/cuppa-alert
Warum nicht einfach die jQuery Dialogbox verwenden?
Nicht nur, dass die jQuery-Box vom Benutzer verschoben werden kann, um Details offenzulegen, die für eine modale Entscheidung erforderlich sind, sondern die UI fängt auch die Tab-Taste ab, um nur zwischen den Schaltflächen in der Dialogbox zu wechseln.
Ich würde es hassen, der Programmierer zu sein, der den "fix this alert box"-Code mit den vielen Details und dem Wissen erbt, das Sie darin eingebettet haben. Ich würde lieber etwas verwenden, das 1000 andere Websites verwenden und von einer Crew gewartet wird, besonders wenn sich Webbrowser/Standards ändern. (Natürlich, wenn *das* kaputt geht, habe ich auch ein Problem, aber zumindest haben es viele andere Leute auch, und das erhöht die Wahrscheinlichkeit, dass jemand genug Details kennt, um es zu reparieren).
Ich habe das im Mai letzten Jahres gemacht.
Hier ist, wie ich es gemacht habe
Code: https://github.com/daemondevin/cdn/blob/main/ModalDialog.js
Demo: https://codepen.io/daemondevin/pen/mdpjzGQ?editors=0100
Wie kann ich ihn schließen lassen, wenn ein Benutzer außerhalb des Modals klickt?
Sie können nicht außerhalb eines echten Modals klicken, und meiner Meinung nach sollten Sie das auch nicht tun. Es ist in Ordnung, außerhalb eines "Menü-Overlays" oder ähnlichem zu klicken, aber nicht bei Modals wie "alert" oder "confirm". Für "Klick außerhalb" fügen Sie normalerweise einen Event-Listener zum Body hinzu, prüfen dann, ob der event.target-Knoten innerhalb des Modals existiert. Wenn nicht, schließen Sie ihn (und entfernen Sie den Listener).