Ersetzen Sie JavaScript-Dialogfelder durch das neue HTML-Dialogfeld Element

Avatar of Mads Stoumann
Mads Stoumann am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

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> mit z-index: 99999;.
  • Es ist mit der Tastatur zugänglich. Drücken Sie Enter zum Akzeptieren und Escape zum 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 Tab erreicht 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 der Tab-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 Dialogfeld open ist 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=&quot;hidden&quot;]),[tabindex]:not([tabindex=&quot;-1&quot;])')]
}

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

Showing the eye icon in DevTools, highlighted in bright green.
Klicken auf das "Auge"-Symbol

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

Showing how to replace JavaScript dialogs that use the alert method. The modal is white against a gray background. The content reads please refresh your browser and is followed by a blue button with a white label that says OK.
alert()
Showing how to replace JavaScript dialogs that use the confirm method. The modal is white against a gray background. The content reads please do you want to continue? and is followed by a black link that says cancel, and a blue button with a white label that says OK.
confirm()
Showing how to replace JavaScript dialogs that use the prompt method. The modal is white against a gray background. The content reads the meaning of life, and is followed by a a text input filled with the number 42, which is followed by a black link that says cancel, and a blue button with a white label that says OK.
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!