Signatur
Beschreibung
Das Ereignis inputsourceschange wird an eine XRSession gesendet, sobald sich die Liste der verfügbaren Eingabequellen ändert. Das passiert z. B., wenn ein Controller eingeschaltet oder abgeschaltet wird, wenn die Handerkennung eine Hand erfasst oder verliert oder wenn der Nutzer ein Gerät die Hand wechseln lässt. Es wird auch unmittelbar nach dem Start einer Sitzung ausgelöst, sobald die ersten Eingabequellen erkannt werden.
Der Handler bekommt ein XRInputSourcesChangeEvent. Seine Eigenschaften added und removed enthalten Arrays von XRInputSource-Objekten, die seit der letzten Änderung neu dazugekommen bzw. weggefallen sind. session verweist auf die betroffene Sitzung. Die vollständige aktuelle Liste steht jederzeit in XRSession.inputSources.
Typische Anwendungen sind das Laden und Entfernen von Controller-Modellen in der Szene, das Anlegen von Zeigestrahlen (Ziellinien) für jede targetRayMode-Art sowie das Anpassen der Bedienung je nach handedness ("left", "right", "none"). Du kannst den Handler entweder mit addEventListener() registrieren oder über die Eigenschaft onleinputsourceschange – korrekt: session.oninputsourceschange.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $type Pflicht | string | Der Ereignistyp, hier "inputsourceschange". |
|
| $handler Pflicht | (event: XRInputSourcesChangeEvent) => void | Callback-Funktion, die ein XRInputSourcesChangeEvent mit den Eigenschaften added, removed und session erhält. |
|
| $options | boolean|AddEventListenerOptions | false | Optionale Listener-Optionen wie once oder signal (zum Entfernen per AbortController). |
Rückgabewert
addEventListener() gibt nichts zurück. Der Handler erhält ein XRInputSourcesChangeEvent; es ist nicht abbrechbar und steigt nicht auf (kein Bubbling).Beispiele
Hinzugekommene und entfernte Controller protokollieren
const startXR = async () => {
const session = await navigator.xr.requestSession("immersive-vr");
session.addEventListener("inputsourceschange", (event) => {
for (const source of event.added) {
console.log(`Neu: ${source.handedness} (${source.targetRayMode})`, source.profiles);
}
for (const source of event.removed) {
console.log(`Entfernt: ${source.handedness}`);
}
console.log("Aktiv insgesamt:", event.session.inputSources.length);
});
};
// Muss aus einer Nutzeraktion heraus aufgerufen werden
document.querySelector("#vr-button").addEventListener("click", startXR);
Controller-Modelle per Map verwalten (on-Eigenschaft)
const controllerModels = new Map();
const createModel = (source) => ({ name: `${source.handedness}-controller`, visible: true });
const handleInputChange = ({ added, removed }) => {
added.forEach((source) => {
// Handverfolgung ohne Controller-Modell
if (source.hand) return;
controllerModels.set(source, createModel(source));
});
removed.forEach((source) => {
controllerModels.delete(source);
});
console.log("Modelle:", [...controllerModels.values()].map((m) => m.name));
};
const session = await navigator.xr.requestSession("immersive-vr", {
optionalFeatures: ["hand-tracking"],
});
session.oninputsourceschange = handleInputChange;
Listener beim Sitzungsende per AbortController entfernen
const controller = new AbortController();
const session = await navigator.xr.requestSession("immersive-ar");
session.addEventListener(
"inputsourceschange",
(e) => console.log("Änderung:", e.added.length, "neu,", e.removed.length, "entfernt"),
{ signal: controller.signal }
);
session.addEventListener("end", () => {
controller.abort(); // entfernt den Listener
// Alternative: session.removeEventListener("inputsourceschange", handler)
console.log("Sitzung beendet, Listener entfernt");
}, { once: true });
// Wichtig · Fallstricke
- Identität der Objekte:
XRInputSource-Objekte bleiben stabil, solange die Quelle verbunden ist. Deshalb eignen sie sich als Schlüssel in einerMap. Ändert sich aberhandednessoderprofiles, meldet der Browser die Quelle als entfernt und neu hinzugekommen – mit einem neuen Objekt. - Nicht für Tasten gedacht: Tastendrücke kommen über
select/squeeze(und die zugehörigen Start-/End-Events) odersource.gamepadin der Frame-Schleife, nicht über dieses Ereignis. - Posen nur im Frame: Die Positionen von
targetRaySpace/gripSpacelassen sich nur inrequestAnimationFramemitframe.getPose()abfragen, nicht direkt im Event-Handler. - Erste Quellen: Beim Sitzungsstart kann
inputSourceszunächst leer sein. Verlass dich also auf das Ereignis und nicht auf eine einmalige Abfrage direkt nachrequestSession(). - Kompatibilität: WebXR braucht einen sicheren Kontext (HTTPS) und läuft vor allem in Chromium-Browsern und im Meta Quest Browser. Firefox (Desktop) und Safari (macOS) unterstützen es nicht; auf der Apple Vision Pro ist WebXR in Safari nur teilweise verfügbar. Prüfe vorher mit
navigator.xr?.isSessionSupported(). Handverfolgung musst du alsoptionalFeatures: [\"hand-tracking\"]anfordern.