Dialogfeld

Übersicht

Verwenden Sie das native <dialog>-Element für modale und nicht modale Dialogfelder. Es bietet die Semantik für Barrierefreiheit, die Fokusbehandlung, das Rendering der obersten Ebene und die Behandlung von Esc, die benutzerdefinierte Overlays manuell neu erstellen müssen.

Overlay-Typ auswählen

Wählen Sie die erste Regel aus, die zutrifft. Wenn Sie sich nicht sicher sind, verwenden Sie Regel 1.

Regel 1: Das Overlay unterbricht den Nutzer und blockiert die Seite (Standardeinstellung)

Verwenden Sie diese Option für Bestätigungen, Formulare, Anmeldungen, destruktive Aktionen und alle „modalen“, „Dialog-“, „Lightbox-“ oder „Pop-up-“Anfragen, sofern nicht anders angegeben.

Verwenden Sie <dialog> als modales Dialogfeld, deklarativ mit command="show-modal" (siehe Deklarative Dialog- und Popover-Steuerung) oder aus einem Skript mit showModal().

<button type="button" command="show-modal" commandfor="confirm">Delete</button>

<!-- MANDATORY: no role="dialog" or aria-modal; the native modal already provides them -->
<dialog id="confirm" aria-labelledby="confirm-title">
  <h2 id="confirm-title">Delete this item?</h2>
  <form method="dialog">
    <button value="cancel" autofocus>Cancel</button>
    <button value="delete">Delete</button>
  </form>
</dialog>

Der Browser bietet: inerte Seite, Fokus-Eingrenzung, oberste Ebene, ::backdrop, Esc und Schließen per Zurück-Geste sowie Rückgabe des Fokus an den Aufrufer.

  • Fügen Sie KEINE role="dialog", aria-modal, Fokusfalle, inert-Umschaltung oder Esc-Handler hinzu.
  • Öffnen Sie sie NICHT mit show() oder dem Attribut open.
  • DO stop the page scrolling behind it with the CSS below. Weitere Informationen finden Sie unter Verhindern, dass die Seite unter einem modalen Dialogfeld gescrollt wird.
  • NICHT das Scrollen mit overflow: hidden oder position: fixed auf html oder body sperren.
/* MANDATORY (both rules): without them, scrolling over the backdrop or inside the dialog scrolls the page behind it */
dialog:modal {
  overscroll-behavior: contain; /* stops scroll gestures in the dialog chaining to the page */
}
dialog::backdrop {
  overflow: hidden; /* ::backdrop is not a scroll container by default, so it would ignore overscroll-behavior */
  overscroll-behavior: contain;
}

Nicht für Menüs oder Kurzinfos: siehe Regel 3.

Regel 2: Der Nutzer arbeitet auf der Seite weiter, während der Bereich geöffnet ist.

Wird für Suchleisten, Prüfer, Paletten und Erstellungsbereiche verwendet, die semantisch noch Dialogfelder sind.

Verwenden Sie <dialog popover> für ein oberstes Steuerfeld mit Pop-over-Schließung (auto schließt leicht, manual nicht). Verwenden Sie dialog.show() nur, wenn sich das Steuerfeld nicht in der obersten Ebene befinden darf.

<button type="button" popovertarget="find">Find</button>
<dialog id="find" popover="manual" aria-labelledby="find-title">
  <h2 id="find-title">Find</h2>
  <input type="search" aria-label="Search">
  <button type="button" popovertarget="find" popovertargetaction="hide">Close</button>
</dialog>

Die Seite bleibt interaktiv und wird nicht inaktiv.

  • DO include a visible close button.
  • Verwenden Sie NICHT showModal(), da dadurch die Seite blockiert wird.

Nicht für Bestätigungen von Blockierungen: siehe Regel 1.

Regel 3: Das Overlay ist kein Dialogfeld

Für Menüs, Drop-down-Menüs, Kurzinfos, Hovercards und Toasts verwenden.

Verwenden Sie ein semantisches Element mit dem Attribut popover, nicht <dialog>.

<button type="button" popovertarget="menu">Actions</button>
<ul id="menu" popover>
  <li><a href="/rename">Rename</a></li>
</ul>

Ausschaltmethode auswählen

Die Standardeinstellung hängt davon ab, wie das Dialogfeld geöffnet wird. Wenn closedby fehlt oder ungültig ist, verhält sich showModal() wie closerequest und jede andere Art des Öffnens wie none.

  • Nur Esc- und Zurück-Geste (modaler Standard): Legen Sie nichts oder closedby="closerequest" fest.
  • Auch durch Klicken außerhalb schließen: Verwenden Sie closedby="any".
  • Nur der Entwickler schließt es (z. B. ein Formular mit nicht gespeicherten Änderungen): Verwenden Sie closedby="none" und fügen Sie einen Bestätigungspfad hinzu. Siehe „Schluss“ unten.

Richtlinien

Barrierefreier Name und erster Schwerpunkt

  • DO name every dialog with aria-labelledby pointing at its heading, or aria-label when there is no visible title.
  • DO put autofocus on the primary control or first field.
  • RICHTIG: Setzen Sie autofocus bei einer Bestätigung mit destruktiver Aktion auf die sichere Aktion (z. B. „Abbrechen“ oder „Behalten“).
  • Schreiben Sie KEINEN Code, um den Fokus beim Schließen zurückzugeben. Der Browser gibt sie an den Aufrufer zurück.

Opening

  • RICHTIG: Deklarativ mit command="show-modal" und commandfor auf einem <button> öffnen. Weitere Informationen finden Sie unter Declarative Dialog and Popover Control.
  • Verwenden Sie dialog.showModal(), wenn Sie die Datei über ein Skript öffnen.

Closing

  • DO close with <form method="dialog"> buttons or command="close".
  • DO choose closedby deliberately: any (light dismiss and Esc), closerequest (Esc only, the modal default) or none (developer-controlled close only).
  • VERWENDEN Sie command="request-close" oder requestClose() für Abbrechen-Schaltflächen, die die Logik für nicht gespeicherte Änderungen berücksichtigen müssen. Sie lösen zuerst cancel aus und ignorieren closedby.
  • DO treat closedby as progressive enhancement. Esc und die Zurück-Geste auf Mobilgeräten schließen Modals bereits ohne diese Taste.

Hooking into close

  • DO: Lies dialog.returnValue in einem close-Listener, um zu erfahren, welche Taste verwendet wurde, und setze sie dann mit dialog.returnValue = '' zurück. Bei Esc und anderen ähnlichen Anfragen wird kein Wert festgelegt. Browser unterscheiden sich darin, ob sie den vorherigen Wert löschen. Ohne das Zurücksetzen kann also ein veralteter Wert weitergegeben werden. '' als „geschlossen, ohne eine Auswahl zu treffen“ behandeln.
  • DO use cancel to intercept Esc and request-close, but do not rely on preventDefault() for repeated Esc presses without user activation.
  • EMPFOHLEN: Verwenden Sie closedby="none" mit einem expliziten Bestätigungsschritt für geänderte Formulare.
  • DO: Informieren Sie den Nutzer, warum das Dialogfeld geöffnet geblieben ist, wenn cancel verhindert wird. Zeigen Sie eine sichtbare Meldung an und verweisen Sie mit aria-describedby auf das Steuerelement, das den Fokus erhält (z. B. „Verwerfen“), damit ein Screenreader die Meldung liest, wenn sich der Fokus verschiebt. Ohne diese Funktion wird beim Verschieben des Fokus auf eine neue Schaltfläche nur der Name dieser Schaltfläche angesagt.
  • NICHT die Bestätigung die Steuerelemente unter den Mauszeiger verschieben lassen. Reservieren Sie den Platz für die Nachricht (blenden Sie sie mit visibility aus, nicht mit display oder hidden) und fügen Sie die destruktive Schaltfläche so hinzu, dass sich vorhandene Schaltflächen nicht verschieben (z. B. als erste in einer rechtsbündigen Zeile). So kann ein zweiter Klick an derselben Stelle die Änderungen des Nutzers nicht verwerfen.

Nicht modale Dialogfelder

  • VERWENDEN Sie <dialog popover> (auto oder manual) für ein Panel auf der obersten Ebene und dialog.show(), wenn Sie die oberste Ebene nicht möchten.
  • DO include a visible close button in every non-modal dialog.

Hintergrund und Styling

Bedienungshilfen

  • Fügen Sie einem nativen modalen Dialogfeld KEINE role="dialog", aria-modal, manuelle Fokusfalle oder inert-Umschaltung hinzu.
  • DO use a native <button> with an accessible name for close controls.
  • EMPFOHLEN: Verwenden Sie für Dialogfeldtitel eine einheitliche Überschriftenebene.

Fallback-Strategien

closedby (dialog-closedby)

Ohne closedby wird ein modales Fenster weiterhin mit Esc und der Zurück-Geste geschlossen. Nur das Klicken außerhalb des Fensters geht verloren. Es handelt sich also um eine graduelle Fehlertoleranz. closedby="none" wird auf die gleiche Weise ignoriert: Esc löst cancel aus und schließt dann das Dialogfeld. Behalten Sie den Schutz für nicht gespeicherte Änderungen in einem cancel-Handler bei, nicht nur in closedby="none", damit er auch dort schützt, wo closedby fehlt. Wenn für Ihr Baseline-Ziel ein Schließen durch Tippen erforderlich ist, fügen Sie Folgendes hinzu, wo closedby fehlt:

if (!('closedBy' in HTMLDialogElement.prototype)) {
  // Test the pointer position, not event.target: the <dialog> is also the target for its own
  // padding and the gaps between its children, so a target check would close on those clicks
  const outside = (e, dialog) => {
    const r = dialog.getBoundingClientRect();
    return e.clientX < r.left || e.clientX > r.right || e.clientY < r.top || e.clientY > r.bottom;
  };
  for (const dialog of document.querySelectorAll('dialog[closedby="any"]')) {
    let pressedOutside = false;
    dialog.addEventListener('pointerdown', (e) => { pressedOutside = outside(e, dialog); });
    // Require the press and the release outside, so a text-selection drag that ends on the backdrop does not dismiss
    dialog.addEventListener('pointerup', (e) => { if (pressedOutside && outside(e, dialog)) dialog.close(); });
  }
}

overscroll-behavior

Behandeln Sie die Scroll-Eingrenzung als progressive Verbesserung. Verwenden Sie keine Feature-Erkennung: @supports meldet das Attribut als unterstützt, auch wenn es von nicht scrollbaren Containern ignoriert wird. Wenn die Funktion nicht erzwungen wird, kann im Dialogfeld weiterhin gescrollt werden, wenn der Inhalt überläuft. Außerdem wird die Seite durch showModal() weiterhin inaktiv. Verwende nicht overflow: hidden oder position: fixed für html oder body, da dadurch die Stamm-Scrollleiste entfernt und das Layout verschoben wird.

Aufruferbefehle (command / commandfor)

Wenn Ihr Baseline-Ziel keine Invoker-Befehle enthält, behalten Sie das Markup bei und laden Sie das Polyfill bedingt (dieselbe Funktionalität, als progressive Verbesserung). Erkenne die Schnittstelle und lade sie nie bedingungslos:

// MANDATORY: feature detect on HTMLButtonElement.prototype and load only when native support is missing
if (!('commandForElement' in HTMLButtonElement.prototype)) {
  import('invokers-polyfill'); // npm install invokers-polyfill
}

Nur die Regeln 2 und 3 verwenden Popover.

Fallbacks und Browserunterstützung für Popover

Die Popover API ist größtenteils eine progressive Verbesserung, aber ihre definierenden Verhaltensweisen – die Hochstufung auf die oberste Ebene, das Schließen durch Klicken außerhalb des Popovers und der Aufruf von popovertarget – haben kein CSS-Äquivalent. Ältere Browser benötigen ein Polyfill oder ein manuelles Fallback, wenn Sie kein Polyfill bereitstellen möchten.

Polyfill: Damit das Attribut popover in älteren Browsern unterstützt wird, lade @oddbird/popover-polyfill bedingt. OBLIGATORISCH: Prüfe, ob die Funktion unterstützt wird, indem du nach der Property popover in HTMLElement.prototype suchst, und lade das Polyfill nur, wenn die native Unterstützung fehlt. Lade es NICHT bedingungslos.

Mit einem Bundler oder einer Importzuordnung:

// MANDATORY: Feature detect 'popover' on HTMLElement.prototype.
if (!("popover" in HTMLElement.prototype)) {
  import("@oddbird/popover-polyfill");
}

Ohne Bundler importieren Sie aus einem CDN in einem <script type="module">:

<script type="module">
  if (!("popover" in HTMLElement.prototype)) {
    import("https://unpkg.com/@oddbird/popover-polyfill@latest/dist/popover.min.js");
  }
</script>

Einschränkung beim Styling: Das Polyfill kann die echte Pseudoklasse :popover-open nicht definieren, daher wird stattdessen die Klasse .\:popover-open angewendet. OBLIGATORISCH: Kombinieren Sie die beiden mit :is() oder :where(). Andernfalls verwerfen Browser, die :popover-open nicht unterstützen, die gesamte Regel:

[popover]:is(:popover-open, .\:popover-open) {
  display: block;
}

Alternativ können Sie für einen Legacy-Fallback ohne Polyfill position: fixed verwenden und die Koordinaten manuell über getBoundingClientRect() berechnen oder die Standardpositionierung mit inset: auto verwenden, wenn das für den Anwendungsfall akzeptabel ist.