Signatur
Beschreibung
Das Ereignis voiceschanged gehört zur Web Speech API und wird am globalen SpeechSynthesis-Objekt (window.speechSynthesis) ausgelöst. Das passiert, wenn sich die Liste der SpeechSynthesisVoice-Objekte ändert, die getVoices() zurückgibt. Meist geschieht das kurz nach dem Laden der Seite, weil viele Browser (vor allem Chrome) die Stimmen asynchron laden. Direkt nach dem Seitenaufruf liefert getVoices() dort oft noch ein leeres Array.
Registrieren kannst du das Ereignis mit speechSynthesis.addEventListener("voiceschanged", handler) oder über die Eigenschaft speechSynthesis.onvoiceschanged = handler. Der Handler bekommt ein einfaches Event-Objekt ohne eigene Zusatzdaten. Die aktuelle Liste holst du im Handler erneut mit getVoices().
Typische Einsatzfälle sind das Füllen eines Auswahlmenüs mit Stimmen, das Auswählen einer bestimmten Stimme (z. B. deutsch, lang === "de-DE") vor dem ersten Vorlesen und das Aktualisieren der Oberfläche, wenn das Betriebssystem Stimmen nachinstalliert oder entfernt.
Wichtig: Das Ereignis kommt nicht in jedem Browser zuverlässig. In manchen Browsern ist die Liste sofort verfügbar, dann wird voiceschanged eventuell gar nicht ausgelöst. Robuster Code liest die Liste deshalb einmal sofort und zusätzlich im Handler.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $type Pflicht | string | Der Name des Ereignisses: "voiceschanged". |
|
| $handler Pflicht | (event: Event) => void | Callback, der bei jeder Änderung der Stimmenliste aufgerufen wird. Er erhält ein einfaches Event-Objekt ohne eigene Zusatzdaten. |
|
| $options | boolean | AddEventListenerOptions | false | Optionen wie bei addEventListener, z. B. { once: true } oder { signal }. |
Rückgabewert
addEventListener gibt nichts zurück. Die neue Stimmenliste liest du im Handler mit speechSynthesis.getVoices().Beispiele
Stimmen robust laden (sofort und bei Änderung)
const loadVoices = () => {
const voices = speechSynthesis.getVoices();
console.log(`${voices.length} Stimmen verfügbar`);
return voices;
};
// Sofort versuchen – manche Browser liefern die Liste direkt
loadVoices();
// Zusätzlich auf asynchrones Nachladen reagieren (z. B. Chrome)
speechSynthesis.addEventListener("voiceschanged", loadVoices);
Auswahlmenü mit Stimmen füllen (on-Property)
const select = document.querySelector("#voice-select");
const populateVoiceList = () => {
const voices = speechSynthesis.getVoices();
select.replaceChildren(
...voices.map((voice) => {
const option = document.createElement("option");
option.value = voice.name;
option.textContent = `${voice.name} (${voice.lang})${voice.default ? " – Standard" : ""}`;
return option;
})
);
};
populateVoiceList();
if ("onvoiceschanged" in speechSynthesis) {
speechSynthesis.onvoiceschanged = populateVoiceList;
}
Promise-Helfer: auf deutsche Stimme warten und vorlesen
const getVoicesAsync = (timeout = 2000) =>
new Promise((resolve) => {
const voices = speechSynthesis.getVoices();
if (voices.length) return resolve(voices);
const onChange = () => {
clearTimeout(timer);
resolve(speechSynthesis.getVoices());
};
// Fallback, falls das Ereignis nie ausgelöst wird
const timer = setTimeout(() => {
speechSynthesis.removeEventListener("voiceschanged", onChange);
resolve(speechSynthesis.getVoices());
}, timeout);
speechSynthesis.addEventListener("voiceschanged", onChange, { once: true });
});
const speakGerman = async (text) => {
const voices = await getVoicesAsync();
const utterance = new SpeechSynthesisUtterance(text);
utterance.voice = voices.find((v) => v.lang.startsWith("de")) ?? null;
utterance.lang = "de-DE";
speechSynthesis.speak(utterance);
console.log("Stimme:", utterance.voice?.name ?? "Standard");
};
speakGerman("Hallo und willkommen!");
Listener wieder entfernen
const logVoices = () => console.log(speechSynthesis.getVoices().length);
speechSynthesis.addEventListener("voiceschanged", logVoices);
// Später, z. B. beim Abbau einer Komponente:
speechSynthesis.removeEventListener("voiceschanged", logVoices);
// bzw. bei der on-Property:
speechSynthesis.onvoiceschanged = null;
// Wichtig · Fallstricke
- Nicht auf das Ereignis allein verlassen: In Firefox und Safari ist die Liste oft sofort verfügbar, dann wird
voiceschangedeventuell nie ausgelöst. Lies die Liste daher immer auch direkt einmal mitgetVoices(). - Mehrfaches Auslösen: Chrome kann das Ereignis mehrmals auslösen, etwa wenn lokale Stimmen und Netzwerkstimmen nacheinander eintreffen. Handler sollten deshalb idempotent sein, also die Liste komplett neu aufbauen statt Einträge anzuhängen.
- Safari: Ältere Safari-Versionen hatten keine
onvoiceschanged-Eigenschaft. Prüfe sie vorher mit"onvoiceschanged" in speechSynthesis. - Keine Zusatzdaten: Das
Event-Objekt enthält die Stimmen nicht. Du musstgetVoices()erneut aufrufen. - Datenschutz: Die Stimmenliste kann zum Fingerprinting genutzt werden. Manche Browser schränken sie deshalb ein oder liefern nur wenige Stimmen.
- Prüfe vor der Nutzung, ob die API überhaupt vorhanden ist:
if ("speechSynthesis" in window). In Web Workers und Node.js gibt es sie nicht.