Start · Sprachen · JavaScript · Referenz · Element: gesturechange

Element: gesturechange

Ereignis

Wird in Safari laufend ausgelöst, während sich die Finger bei einer Mehrfinger-Geste (Pinch/Drehung) bewegen, und liefert Skalierung und Drehwinkel.

seit JavaScript Nicht standardisiert: nur Safari (iOS Safari 2+, m Kategorie: webapi

Signatur

element.addEventListener("gesturechange", handler)

Beschreibung

Das Ereignis gesturechange gehört zu den proprietären WebKit-Gestenereignissen. Es wird wiederholt auf einem Element ausgelöst, solange eine Mehrfinger-Geste läuft, also zwischen gesturestart und gestureend. Das übergebene GestureEvent enthält die Eigenschaften scale (Skalierungsfaktor relativ zum Beginn der Geste, 1 = unverändert) und rotation (Drehwinkel in Grad relativ zum Beginn, positiv = im Uhrzeigersinn).

Typische Einsatzzwecke sind Zoom per Pinch und das Drehen von Bildern, Karten oder Canvas-Inhalten. Ein Beispiel ist eine Bildergalerie, in der man mit zwei Fingern zoomt. Unter iOS kommen die Ereignisse vom Touchscreen, unter macOS Safari von Pinch- und Drehgesten auf dem Trackpad.

Du kannst den Handler per addEventListener("gesturechange", handler) registrieren oder über die Eigenschaft element.ongesturechange zuweisen. Soll der Browser dabei nicht selbst zoomen, rufst du event.preventDefault() auf. Der Listener muss dann mit { passive: false } registriert werden.

Weil die API nicht standardisiert ist, solltest du sie nur als Verbesserung für Safari nutzen. Für alle anderen Browser brauchst du eine Alternative mit Pointer Events oder Touch Events, bei der du zwei Kontaktpunkte selbst auswertest.

Parameter

Name Typ Default Beschreibung
$type Pflicht string Der Ereignisname "gesturechange".
$handler Pflicht (event: GestureEvent) => void Callback, das bei jeder Bewegung während der Geste aufgerufen wird. Wichtige Eigenschaften: event.scale (number), event.rotation (number, Grad), event.clientX/clientY (Mittelpunkt der Geste), dazu Modifikatortasten wie ctrlKey und shiftKey.
$options boolean | AddEventListenerOptions false Optionen wie { passive: false }, damit preventDefault() den nativen Zoom verhindern kann. Weitere Optionen sind once, capture und signal.

Rückgabewert

Typ
void
Beschreibung
addEventListener gibt nichts zurück. Der Handler erhält ein GestureEvent (erbt von UIEvent).

Beispiele

Bild per Pinch zoomen und drehen (Safari)

const img = document.querySelector('#foto');
let baseScale = 1;
let baseRotation = 0;

img.addEventListener('gesturestart', (e) => {
  e.preventDefault();
}, { passive: false });

img.addEventListener('gesturechange', (e) => {
  e.preventDefault(); // nativen Seitenzoom unterdrücken
  const scale = baseScale * e.scale;
  const rotation = baseRotation + e.rotation;
  img.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
  console.log(`scale=${e.scale.toFixed(2)} rotation=${e.rotation.toFixed(1)}°`);
}, { passive: false });

img.addEventListener('gestureend', (e) => {
  // Endwerte für die nächste Geste merken
  baseScale *= e.scale;
  baseRotation += e.rotation;
});
scale=1.12 rotation=3.4° scale=1.35 rotation=8.9° ...

Handler per on-Property setzen und mit removeEventListener entfernen

const karte = document.querySelector('#karte');

const onGesture = (e) => {
  e.preventDefault();
  console.log('Zoomfaktor:', e.scale.toFixed(2));
};

// Variante 1: addEventListener
karte.addEventListener('gesturechange', onGesture, { passive: false });

// Später wieder abmelden, z. B. wenn der Zoom gesperrt wird
document.querySelector('#sperren').addEventListener('click', () => {
  karte.removeEventListener('gesturechange', onGesture, { passive: false });
  console.log('Gesten-Zoom deaktiviert');
});

// Variante 2: on-Property (ersetzt einen vorherigen Handler)
// karte.ongesturechange = onGesture;
// karte.ongesturechange = null; // entfernen
Zoomfaktor: 1.08 Zoomfaktor: 1.21 Gesten-Zoom deaktiviert

Funktionsprüfung mit Alternative für andere Browser

const flaeche = document.querySelector('#canvas-wrapper');
let zoom = 1;
const apply = () => { flaeche.style.transform = `scale(${zoom})`; };

if ('ongesturechange' in window) {
  // Safari: native Gestenereignisse
  let start = 1;
  flaeche.addEventListener('gesturestart', (e) => { e.preventDefault(); start = zoom; }, { passive: false });
  flaeche.addEventListener('gesturechange', (e) => {
    e.preventDefault();
    zoom = Math.min(Math.max(start * e.scale, 0.5), 4);
    apply();
  }, { passive: false });
} else {
  // Chrome/Firefox/Edge: Trackpad-Pinch kommt als wheel mit ctrlKey
  flaeche.addEventListener('wheel', (e) => {
    if (!e.ctrlKey) return;
    e.preventDefault();
    zoom = Math.min(Math.max(zoom * Math.exp(-e.deltaY / 100), 0.5), 4);
    apply();
  }, { passive: false });
}
console.log('Gesten-API:', 'ongesturechange' in window ? 'WebKit' : 'Alternative');
Gesten-API: WebKit (in Safari) Gesten-API: Alternative (in anderen Browsern)

// Wichtig · Fallstricke

  • Nicht standardisiert: Nur Safari unter iOS/iPadOS und macOS unterstützt das Ereignis. Chrome, Firefox und Edge kennen es nicht, auch nicht unter Android. Prüfe die Unterstützung mit 'ongesturechange' in window oder typeof GestureEvent !== 'undefined'.
  • scale und rotation beziehen sich immer auf den Beginn der aktuellen Geste und sind keine schrittweisen Änderungen. Speichere die Endwerte in gestureend, sonst springt das Element bei der nächsten Geste auf den Ausgangszustand zurück.
  • preventDefault() wirkt nur, wenn der Listener nicht passiv ist ({ passive: false }). Seit iOS 10 ignoriert Safari außerdem user-scalable=no im Viewport-Meta-Tag. Das Abfangen der Gestenereignisse ist dann oft der einzige Weg, den Seitenzoom zu unterdrücken. Das schadet aber der Barrierefreiheit, also nur gezielt auf einzelnen Elementen einsetzen.
  • Unter iOS feuern parallel auch touchstart, touchmove und touchend. Wer beide Ereignisarten verarbeitet, muss doppelte Reaktionen vermeiden.
  • Das Ereignis feuert sehr oft. Für aufwendige Neuzeichnungen die Werte zwischenspeichern und nur in requestAnimationFrame anwenden. CSS-transform ist günstiger als Layout-Änderungen.