Start · Sprachen · JavaScript · Referenz · CloseWatcher: cancel

CloseWatcher: cancel

Ereignis

Wird an einem <code>CloseWatcher</code> vor dem <code>close</code>-Ereignis ausgelöst. Mit <code>preventDefault()</code> lässt sich das Schließen abbrechen, etwa für eine Rückfrage.

seit JavaScript Chromium 120+ (Chrome, Edge, Opera); in Firefox un Kategorie: webapi

Signatur

closeWatcher.addEventListener("cancel", handler)

Beschreibung

Das cancel-Ereignis gehört zur CloseWatcher-API. Mit ihr reagieren eigene UI-Komponenten wie Seitenleisten, Popover oder selbst gebaute Dialoge auf die üblichen Schließ-Anfragen der jeweiligen Plattform: die Esc-Taste am Desktop, die Zurück-Geste bzw. Zurück-Taste unter Android oder einen Aufruf von closeWatcher.requestClose(). Bevor das eigentliche close-Ereignis kommt, löst der Browser cancel aus. An dieser Stelle entscheidet die Anwendung, ob wirklich geschlossen wird.

Ruft der Handler event.preventDefault() auf, wird das Schließen abgebrochen. close wird dann nicht ausgelöst, und der Watcher bleibt aktiv. Ein typischer Fall: Ein Formular enthält ungespeicherte Änderungen, und der Nutzer soll erst bestätigen, dass die Eingaben verloren gehen dürfen.

Wichtig: Abbrechen lässt sich nur, wenn event.cancelable den Wert true hat. Das ist nur der Fall, wenn die Seite seit der letzten Schließ-Anfrage eine Nutzeraktivierung erhalten hat, also etwa einen Klick oder Tastendruck. So können Seiten den Nutzer nicht mit der Zurück-Taste „einsperren“. Ohne Aktivierung wird cancel trotzdem ausgelöst, aber mit cancelable === false, und preventDefault() bewirkt nichts.

Einen Handler kann man mit addEventListener("cancel", …) registrieren oder über die Eigenschaft closeWatcher.oncancel zuweisen. Das Ereignis ist ein einfaches Event. Es steigt nicht auf (kein Bubbling), denn es wird direkt am CloseWatcher-Objekt ausgelöst.

Parameter

Name Typ Default Beschreibung
$type Pflicht string Der Ereignistyp, hier immer "cancel".
$handler Pflicht (event: Event) => void | EventListenerObject Callback, das vor dem Schließen aufgerufen wird. Mit event.preventDefault() bricht man das Schließen ab, sofern event.cancelable den Wert true hat.
$options boolean | { once?: boolean, passive?: boolean, signal?: AbortSignal } false Übliche Listener-Optionen. Mit signal lässt sich der Listener zusammen mit anderen Aufräumarbeiten entfernen. passive: true würde preventDefault() wirkungslos machen und ist hier daher nicht sinnvoll.

Rückgabewert

Typ
void
Beschreibung
addEventListener gibt nichts zurück. Der Handler erhält ein Event-Objekt mit type === "cancel". Entscheidend ist dessen Eigenschaft cancelable.

Beispiele

Ungespeicherte Änderungen vor dem Schließen bestätigen

const panel = document.querySelector('#edit-panel');
const form = panel.querySelector('form');
let dirty = false;

form.addEventListener('input', () => { dirty = true; });

const watcher = new CloseWatcher();

watcher.addEventListener('cancel', (event) => {
  // Nur abbrechen, wenn es erlaubt ist und wirklich etwas verloren ginge
  if (event.cancelable && dirty) {
    const ok = confirm('Änderungen verwerfen?');
    if (!ok) event.preventDefault();
  }
});

watcher.addEventListener('close', () => {
  panel.hidden = true;
  console.log('Panel geschlossen');
});

// Eigener Schließen-Button: requestClose() durchläuft ebenfalls cancel
panel.querySelector('.close-btn').addEventListener('click', () => {
  watcher.requestClose();
});
Panel geschlossen // nur wenn nichts geändert wurde oder der Nutzer bestätigt hat

oncancel-Eigenschaft mit Feature-Erkennung

const openSidebar = (sidebar) => {
  sidebar.classList.add('open');

  if (!('CloseWatcher' in window)) {
    // Rückfall: nur Esc-Taste behandeln
    const onKey = (e) => {
      if (e.key === 'Escape') {
        sidebar.classList.remove('open');
        document.removeEventListener('keydown', onKey);
      }
    };
    document.addEventListener('keydown', onKey);
    return;
  }

  const watcher = new CloseWatcher();
  watcher.oncancel = (event) => {
    console.log('cancel, abbrechbar:', event.cancelable);
  };
  watcher.onclose = () => sidebar.classList.remove('open');
};

openSidebar(document.querySelector('#sidebar'));
cancel, abbrechbar: true // nach vorheriger Nutzerinteraktion

Listener per AbortSignal entfernen und Watcher abbauen

const controller = new AbortController();
const watcher = new CloseWatcher({ signal: controller.signal });

const onCancel = (event) => {
  if (event.cancelable) event.preventDefault();
  console.log('Schließen verhindert');
};

// Variante 1: gezielt entfernen
watcher.addEventListener('cancel', onCancel);
watcher.removeEventListener('cancel', onCancel);

// Variante 2: der Abbruch des Signals zerstört den Watcher (wie destroy()),
// danach werden weder cancel noch close ausgelöst
watcher.addEventListener('cancel', onCancel);
controller.abort();
watcher.requestClose(); // bewirkt nichts mehr

// Wichtig · Fallstricke

  • Nutzeraktivierung erforderlich: event.cancelable ist nur true, wenn seit der letzten Schließ-Anfrage eine Nutzerinteraktion stattgefunden hat. Wer preventDefault() aufruft, verbraucht diese Aktivierung. Eine zweite Schließ-Anfrage ohne neue Interaktion schließt deshalb ohne Rückfrage. Prüfen Sie immer event.cancelable, bevor Sie einen Dialog anzeigen.
  • Nicht jeder Weg löst cancel aus: closeWatcher.close() überspringt cancel und löst sofort close aus. destroy() bzw. ein abgebrochenes signal löst gar kein Ereignis aus. Nur Schließ-Anfragen vom Nutzer und requestClose() durchlaufen cancel.
  • Kein Ersatz für beforeunload: cancel schützt nur vor dem Schließen der Komponente, nicht vor dem Verlassen der Seite.
  • Gruppierung: Mehrere ohne Nutzeraktivierung erzeugte Watcher können zu einer Gruppe zusammengefasst werden, sodass eine einzige Zurück-Geste mehrere von ihnen schließt.
  • Kompatibilität: Unterstützt in Chromium-Browsern ab Version 120. Firefox und Safari bieten die API noch nicht allgemein an. Nutzen Sie deshalb eine Feature-Erkennung mit 'CloseWatcher' in window und einen Rückfall auf keydown mit Escape. Für modale Dialoge ist das eingebaute <dialog> mit seinem eigenen cancel-Ereignis oft die einfachere Wahl.
  • Die Option passive: true macht preventDefault() wirkungslos.