Signatur
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
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);
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`);
}
};
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 });
// Wichtig · Fallstricke
- Frame nur kurz gültig:
event.frameist nur während des Handlers aktiv. WirdgetPose()später aufgerufen, etwa in einemsetTimeoutoder nach einemawait, wirft der Aufruf einenInvalidStateError. Benötigte Daten also sofort auslesen. - Abgebrochene Aktionen: Nicht jedes
selectstartführt zu einemselect. Zustände, die inselectstartgesetzt werden, immer inselectendzurü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:
selectkommt erst beim Loslassen. Für Drag-and-Drop deshalbselectstartundselectendnutzen. - Andere Tasten: Sekundäre Aktionen wie die Grip-Taste lösen stattdessen
squeezeaus. Weitere Tasten fragt man pro Frame überinputSource.gamepadab. - 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.