Start · Sprachen · JavaScript · Referenz · XRSession: select

XRSession: select

Ereignis

Wird an eine <code>XRSession</code> gesendet, wenn eine Eingabequelle ihre primäre Aktion abgeschlossen hat, z. B. einen Trigger-Druck, ein Tippen oder eine Pinch-Geste.

seit JavaScript WebXR Device API – Chromium-Browser (Chrome/Edge 7 Kategorie: webapi

Signatur

xrSession.addEventListener("select", handler)

Beschreibung

Das select-Event ist das wichtigste Interaktions-Event in WebXR. Es wird ausgelöst, wenn eine primäre Aktion einer Eingabequelle (XRInputSource) erfolgreich abgeschlossen wurde. Bei einem Controller ist das meist der Trigger, bei Handtracking eine Pinch-Geste, bei AR auf Smartphones ein Tippen auf den Bildschirm und bei Blicksteuerung ein Klick bzw. das Verweilen.

Die Reihenfolge ist immer gleich: Zuerst kommt selectstart, dann – wenn die Aktion vollständig ausgeführt wurde – select und zum Schluss selectend. Wird die Aktion abgebrochen (z. B. weil der Controller die Verbindung verliert), fehlt select, aber selectend wird trotzdem gesendet. Für einfache „Klick“-Aktionen wie Objekt auswählen, Knopf drücken oder Objekt platzieren reicht deshalb select. Für Ziehen und Halten verwendet man selectstart/selectend.

Der Handler bekommt ein XRInputSourceEvent. Dessen Eigenschaft inputSource verweist auf die auslösende Eingabequelle, und frame enthält einen XRFrame, der nur während des Handlers gültig ist. Mit event.frame.getPose(inputSource.targetRaySpace, referenceSpace) lässt sich der Zielstrahl zum Zeitpunkt der Auswahl bestimmen, etwa für einen Raycast gegen die Szene.

Registrieren kann man den Handler mit addEventListener("select", …) oder über die Eigenschaft xrSession.onselect. Das Event wird nicht weitergereicht (bubbelt nicht) und kann nicht abgebrochen werden.

Parameter

Name Typ Default Beschreibung
$type Pflicht string Der Event-Name "select".
$handler Pflicht (event: XRInputSourceEvent) => void Callback, der ein XRInputSourceEvent mit den Eigenschaften inputSource (XRInputSource) und frame (XRFrame) erhält.
$options boolean | AddEventListenerOptions false Übliche Listener-Optionen wie once oder signal.

Rückgabewert

Typ
void
Beschreibung
addEventListener gibt nichts zurück. Der Handler erhält ein XRInputSourceEvent.

Beispiele

Zielstrahl bei Auswahl ermitteln (addEventListener)

const startXR = async () => {
  if (!(await navigator.xr?.isSessionSupported('immersive-vr'))) {
    console.log('Immersive VR wird nicht unterstützt');
    return;
  }
  const session = await navigator.xr.requestSession('immersive-vr');
  const refSpace = await session.requestReferenceSpace('local');

  session.addEventListener('select', (event) => {
    const { inputSource, frame } = event;
    // Pose des Zielstrahls genau zum Zeitpunkt der Auswahl
    const pose = frame.getPose(inputSource.targetRaySpace, refSpace);
    if (pose) {
      const { x, y, z } = pose.transform.position;
      console.log(`Auswahl mit ${inputSource.handedness}-Hand bei`, x.toFixed(2), y.toFixed(2), z.toFixed(2));
    }
  });
};

document.querySelector('#enter-vr').addEventListener('click', startXR);
Auswahl mit right-Hand bei 0.21 1.35 -0.40

AR: Objekt per Tippen platzieren (onselect-Eigenschaft)

const session = await navigator.xr.requestSession('immersive-ar', {
  requiredFeatures: ['hit-test']
});
const refSpace = await session.requestReferenceSpace('local');
const viewerSpace = await session.requestReferenceSpace('viewer');
const hitTestSource = await session.requestHitTestSource({ space: viewerSpace });

const placedObjects = [];

session.onselect = (event) => {
  const hits = event.frame.getHitTestResults(hitTestSource);
  if (hits.length > 0) {
    const pose = hits[0].getPose(refSpace);
    placedObjects.push(pose.transform.matrix);
    console.log(`Objekt Nr. ${placedObjects.length} platziert`);
  }
};
Objekt Nr. 1 platziert

Listener wieder entfernen und Ablauf der Events

const session = await navigator.xr.requestSession('immersive-vr');

const log = (e) => console.log(e.type, e.inputSource.handedness);

session.addEventListener('selectstart', log);
session.addEventListener('select', log);
session.addEventListener('selectend', log);

// Nach Sitzungsende alle Listener aufräumen
session.addEventListener('end', () => {
  session.removeEventListener('selectstart', log);
  session.removeEventListener('select', log);
  session.removeEventListener('selectend', log);
  // alternativ bei onselect: session.onselect = null;
}, { once: true });
selectstart right select right selectend right

// Wichtig · Fallstricke

  • Frame nur kurz gültig: event.frame ist nur während des Handlers aktiv. Wird getPose() später aufgerufen, etwa in einem setTimeout oder nach einem await, wirft der Aufruf einen InvalidStateError. Benötigte Daten also sofort auslesen.
  • Abgebrochene Aktionen: Nicht jedes selectstart führt zu einem select. Zustände, die in selectstart gesetzt werden, immer in selectend zurücksetzen.
  • Nutzer-Aktivierung: Innerhalb eines select-Handlers gilt eine vorübergehende Nutzer-Aktivierung (transient user activation). Damit lassen sich z. B. Audio starten oder Sitzungsaktionen ausführen, die eine Nutzergeste erfordern.
  • Aktionen mit Halten oder Ziehen: select kommt erst beim Loslassen. Für Drag-and-Drop deshalb selectstart und selectend nutzen.
  • Andere Tasten: Sekundäre Aktionen wie die Grip-Taste lösen stattdessen squeeze aus. Weitere Tasten fragt man pro Frame über inputSource.gamepad ab.
  • Browser-Unterstützung: WebXR funktioniert nur in sicheren Kontexten (HTTPS) und nur in Chromium-basierten Browsern bzw. VR-Browsern. Firefox und Safari (Desktop) unterstützen WebXR nicht, Safari auf der Apple Vision Pro nur teilweise. Vorher mit navigator.xr?.isSessionSupported() prüfen, ob WebXR verfügbar ist.