Modale und nicht-modale Dialoge

Autor: Aykut Şensoy, Datum:

Mit dem dialog-Element und der Fullscreen API bietet HTML5 eine native Möglichkeit an, eine Dialog-Box im Fullscreen-Modus umsetzen. Die Fullscreen API bietet mit show() und showModal() zwei Methoden an, um zu steuern, ob die Dialog-Box nicht-modal oder modal geöffnet wird. Der Hintergrund einer Dialog-Box kann mit dem ::backdrop Pseudo-Element und der Deklaration backdrop-filter gestylt werden. Ich möchte in diesem Tutorial auf die Kernfunktionalität des dialog-Elements und der Fullscreen API eingehen und eine Auswahl von Styling-Möglichkeiten für den Hintergrund einer Dialog-Box zeigen.

Grundlegende Funktionalität

Ein Dialog wird mit dem semantischen dialog-Tag definiert. Das dialog-Element ist initial nicht sichtbar. Es wird mit der Methode show() als nicht-modaler Dialog oder mit showModal() als modaler Dialog geöffnet. Dabei setzt der Browser automatisch im Quellcode das Attribut open an den dialog-Tag. Mit der Methode close() lässt sich der Dialog wieder schließen.

Nicht-Modaler Dialog

<script>
  function openNonModalDialog(element) {
    let nonModalDialog = document.getElementById(element);
    nonModalDialog.show();
  }
  function closeDialog(element) {
    let dialog = document.getElementById(element);
    dialog.close();
  }
</script>

<dialog id="nonModalDialog">
  <p>Nicht-modaler Dialog</p>
  <button onclick="closeDialog('nonModalDialog')">x</button>
</dialog>

<button onclick="openNonModalDialog('nonModalDialog')">Nicht-modalen Dialog öffnen</button>

Nicht-modaler Dialog

Bei einem nicht-modalen Dialog lässt sich der Hintergrund anfassen. Wenn man die Seite scrollt, wird der Dialog mitgescrollt. Es ist NICHT möglich den Dialog mit der esc-Taste auf der Tastatur zu schließen.

Im Folgenden sieht man die browser-internen Styles für den nicht-modalen Dialog. Dabei sieht man, dass das dialog-Element auf display: none; gesetzt ist. Das ist der Grund warum es initial nicht sichtbar ist. Sobald der Browser das Attribut open setzt, greift hier der Attribut-Selektor dialog[open] { display: block; } und der Dialog wird sichtbar. Das dialog-Element wird auf position: absolute; gesetzt. Das ist wiederum der Grund, warum beim Scrolling der Seite der nicht-modale Dialog mitgescrollt wird.

dialog[open] {
  display: block;
}

dialog {
  display: none;
  position: absolute;
  inset-inline-start: 0px;
  inset-inline-end: 0px;
  width: fit-content;
  height: fit-content;
  background-color: canvas;
  color: canvastext;
  margin: auto;
  border-width: initial;
  border-style: solid;
  border-color: initial;
  border-image: initial;
  padding: 1em;
}

Modaler Dialog

Bei einem modalen Dialog lässt sich der Hintergrund NICHT anfassen. Wenn man die Seite scrollt, bleibt der Dialog fix an seiner Position stehen und wird NICHT mit dem Hintergrund mitgescrollt. Mit dem Drücken auf die esc-Taste auf der Tastatur ist es möglich, den modalen Dialog zu schließen.

<script>
  function openModalDialog(element) {
    let modalDialog = document.getElementById(element);
    modalDialog.showModal();
  }
  function closeDialog(element) {
    let dialog = document.getElementById(element);
    dialog.close();
  }
</script>

  <dialog id="modalDialog">
    <p>Modaler Dialog</p>
    <button onclick="closeDialog('modalDialog')">x</button>
  </dialog>
  
  <button onclick="openModalDialog('modalDialog')">Modalen Dialog öffnen</button>
  

Modaler Dialog

Für den modalen Dialog werden zusätzlich die folgenden browser-internen Styles gesetzt. Hier sieht man, dass in diesem Fall position: fixed; definiert wird und somit position: absolute; überschreibt. Das ist der Grund, warum der modale Dialog nicht mitgescrollt wird.

dialog:modal {
  position: fixed;
  inset-block-start: 0px;
  inset-block-end: 0px;
  max-width: calc((100% - 6px) - 2em);
  max-height: calc((100% - 6px) - 2em);
  user-select: text;
  visibility: visible;
  overflow: auto;
}

Hintergrund mit ::backdrop Pseudo-Element

Der Hintergrund einer Dialog-Box kann mit dem Pseudo-Element ::backdrop gestylt werden. Dabei ist allerdings zu beachten, dass ::backdrop nur dann greift, wenn die Dialog-Box auch tatsächlich mit den bereitgestellten Methoden der Fullscreen API geöffnet wird. D.h. wenn man z.B. am dialog-Tag manuell ein open Attribut definiert, funktioniert ::backdrop einfach nicht. Es ist auch stets darauf zu achten, zwei Doppelpunkte vor dem Schlüsselwort "backdrop" zu definieren, damit das Pseudo-Element erwartungsgemäß funktioniert.

Hier sind drei Beispiele für Hintergründe mit ::backdrop. Beim ersten Beispiel handelt es sich sicherlich um die geläufigste Anforderung, einem modalen Dialog mit gedimmten Hintergrund. Dabei wird die Hintergrund-Transparenz mit dem Alpha-Kanal der RGBA Funktion erzeugt. Im zweiten Beispiel wird ein Hintergrundbild als Kachelgrafik definiert. Im dritten Beispiel wird ein Farbverlauf mit CSS gradients definiert. Es ist prinzipiell alles machbar was in Zusammenhang mit CSS background möglich ist. Dazu zählen auch Keyframe-Animationen, Transitions und hover-Effekte. Eine weitere Besonderheit von ::backdrop ist, dass hier optional die Deklaration opacity für die Hintergrund-Transparenz angewendet werden kann, was normalerweise bei CSS background nicht geht, da so etwas wie "background-opacity" nicht existiert.

Modaler Dialog

Modaler Dialog

Modaler Dialog

.modal-dialog-bg-color::backdrop {
  background-color: rgb(0 0 0 / 0.7);
}
.modal-dialog-bg-image::backdrop {
  background-image: url(images/tile.png);
}
.modal-dialog-bg-gradient::backdrop {
  background-image: linear-gradient(0deg, rgba(34, 193, 195, 1) 0%, rgba(253, 187, 45, 1) 100%);
}

Hintergrund-Effekte mit backdrop-filter Eigenschaft

Weiterhin kann der Hintergrund einer Dialog-Box mit der Deklaration backdrop-filter mit Filter-Effekten versehen werden. Im Grunde handelt es sich um die gleichen Möglichkeiten, wie es mit CSS Filtern möglich ist, nur dass sie in diesem Fall speziell auf den Hintergrund von Dialog-Boxen anwendbar sind.

Hier sind drei Beispiele für Hintergründe mit backdrop-filter. Im ersten Beispiel wird der Hintergrund mit dem blur() Filter unscharf dargestellt. Im zweiten Beispiel wird der Hintergrund mit dem grayscale() Filter in Graustufen umgewandelt. Im dritten Beispiel wird der Hintergrund mit dem invert() Filter invertiert. Für iOS-Geräte muss aktuell noch der vendor-prefix mitdefiniert werden. Selbstveständlich ist mit CSS Filtern deutlich mehr möglich, daher empfehle ich hier in einschlägigen Quellen weiterzulesen, da es hier inhaltlich den Rahmen sprengt.

Modaler Dialog

Modaler Dialog

Modaler Dialog

.modal-dialog-bg-blur::backdrop {
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
}
.modal-dialog-bg-grayscale::backdrop {
  -webkit-backdrop-filter: grayscale(1);
  backdrop-filter: grayscale(1);
}
.modal-dialog-bg-invert::backdrop {
  -webkit-backdrop-filter: invert(1);
  backdrop-filter: invert(1);
}

Autofocus

Wenn man eine Dialog-Box öffnet, wird der Fokus automatisch auf das erste fokussierbare Element innerhalb des dialog-Elements gesetzt. D.h. es macht z.B. Sinn, den Schließen-Button als erstes Element innerhalb des dialog-Tags zu platzieren. Somit ist es möglich mit der enter-Taste auf der Tastatur die Dialog-Box zu schließen. Möchte man hingegen den Fokus auf ein anderes Element legen, wie z.B. auf einen Akzeptieren-Button, dann bietet es sich an, das globale Attribut autofocus am gewünschten Element zu definieren, damit der Fokus automatisch auf diesem Element liegt.

Custom Styling

Selbstverständlich kann man auch die browser-internen Styles für das dialog-Element überschreiben, in dem man die Pseudo-Klasse :modal nutzt. Mit der Deklaration inset-inline-start kann z.B. die vertikale Position der Dialog-Box gesteuert werden. Mit width/height bzw. max-width/max-height kann man z.B. die Dimensionen der Dialog-Box beeinflussen, die sich sonst am Content orientieren. Mit CSS border lässt sich der standardmäßig gesetzte Rahmen der Dialog-Box indivuell gestalten.