Zamykanie okna przez kliknięcie poza nim

Funkcje internetowe: <dialog closedby>

Nowoczesne okna modalne często obsługują funkcję „lekkiego zamykania”, która umożliwia użytkownikom zamknięcie okna przez kliknięcie lub dotknięcie tła (obszaru poza oknem). Atrybut closedby umożliwia deklaratywne włączenie tego zachowania bez niestandardowego kodu JavaScript.

Implementacja

Aby włączyć tę funkcję:

  1. Dodaj closedby="any" do elementu <dialog>.
  2. Otwórz okno za pomocą dialog.showModal().

Wartości atrybutów

  • any: umożliwia zamknięcie przez kliknięcie tła, „żądania zamknięcia” (klawisz Esc) i mechanizmy dla deweloperów (np. dialog.close()).
  • closerequest: włącza tylko „żądania zamknięcia” i mechanizmy dla deweloperów. Jest to ustawienie domyślne w przypadku okien modalnych.
  • none: okno mogą zamknąć tylko mechanizmy dewelopera.

Stylizowanie tła

Gdy okno jest otwierane jako modalne za pomocą showModal(), przeglądarka generuje pseudoelement ::backdrop. To tło obejmuje cały widoczny obszar i znajduje się bezpośrednio za oknem.

/* Style the backdrop to indicate the dialog is modal */
dialog::backdrop {
  background-color: rgba(0, 0, 0, 0.5);
  backdrop-filter: blur(2px); /* Optional: add blur for modern browsers */
}

Przykład

<!-- MANDATORY: Use closedby="any" to enable light-dismiss behavior -->
<dialog id="myDialog" closedby="any" aria-labelledby="dialogTitle">
  <form method="dialog">
    <h2 id="dialogTitle">Feedback</h2>
    <p>Click outside this box or press Esc to dismiss.</p>
    <button type="submit">Close</button>
  </form>
</dialog>

<button onclick="document.getElementById('myDialog').showModal()">Open Dialog</button>

Ograniczenia i ułatwienia dostępu

  • OBOWIĄZKOWO: użyj closedby="any", aby deklaratywnie włączyć zamykanie przez kliknięcie poza elementem.
  • OBOWIĄZKOWO: zawsze otwieraj modalne okna dialogowe za pomocą showModal(). Dzięki temu okno dialogowe znajduje się w górnej warstwie, zaznaczenie jest zablokowane, a klawisz Esc jest obsługiwany.
  • DO: Use aria-labelledby or aria-label to provide an accessible name for the dialog.
  • NIE: używaj elementu closedby w przypadku okien dialogowych, które nie są modalne (otwieranych za pomocą show()), ponieważ nie mają one tła i nie wywołują zamknięcia po kliknięciu poza oknem.
  • NIE używaj zdarzenia click do krytycznej logiki, która powinna nastąpić przed zamknięciem. Zamiast tego nasłuchuj zdarzeń close lub cancel.

Strategie zastępcze

OBOWIĄZKOWO: w przypadku przeglądarek, które nie obsługują jeszcze atrybutu closedby, musisz wdrożyć zastępcze rozwiązanie do zamykania okna przez kliknięcie poza nim. Aby sprawdzić, czy kliknięcie nastąpiło poza granicami treści okna, użyj tego skryptu:

const dialog = document.querySelector('dialog');

// Fallback for browsers without closedby support
if (!('closedBy' in HTMLDialogElement.prototype)) {
  dialog.addEventListener('click', (event) => {
    // 1. When clicking the backdrop, the event target is the dialog element itself.
    // Ignore clicks where the target is a child element inside the dialog.
    if (event.target !== dialog) return;

    // 2. Check if the click coordinates fall within the dialog's content box.
    // This distinguishes between a click on the backdrop vs a click on the dialog's background/padding.
    const rect = dialog.getBoundingClientRect();
    const isDialogContent = (
      rect.top <= event.clientY &&
      event.clientY <= rect.top + rect.height &&
      rect.left <= event.clientX &&
      event.clientX <= rect.left + rect.width
    );

    if (isDialogContent) return;

    // 3. Since the click was outside the content area (on the backdrop), manually close the dialog.
    dialog.close();
  });
}