Signatur
Beschreibung
Das Ereignis canplay feuert an einem HTMLMediaElement (also an <audio> oder <video>), sobald der Browser genug Daten hat, um die Wiedergabe zu starten. Das entspricht einem readyState von mindestens HAVE_FUTURE_DATA (3). Der Browser schätzt dabei aber, dass die Daten nicht reichen, um das Medium ohne weiteres Puffern bis zum Ende abzuspielen. Wenn er glaubt, dass es ohne Unterbrechung klappt, feuert er zusätzlich canplaythrough.
Typische Anwendungsfälle:
- Einen Lade-Spinner ausblenden und den Play-Button freischalten
- Die Wiedergabe per Skript starten, sobald sie möglich ist
- Ladezeiten messen, etwa die Zeit bis zum ersten abspielbaren Zustand
Du kannst das Ereignis mit addEventListener("canplay", handler) abonnieren oder über die Eigenschaft oncanplay setzen. Es lässt sich nicht abbrechen und steigt nicht im DOM auf (bubbles: false). Delegation über ein Elternelement klappt also nur in der Capture-Phase.
Das Ereignis kann mehrfach feuern, zum Beispiel nach einem Sprung (seeked), nach einem Pufferengpass (waiting) oder nach einem Wechsel der Quelle. Code, der nur einmal laufen soll, registrierst du deshalb mit { once: true }.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $type Pflicht | string | Der Ereignisname "canplay". |
|
| $handler Pflicht | (event: Event) => void | Callback mit einem einfachen Event-Objekt. event.target ist das Medienelement. |
|
| $options | boolean | AddEventListenerOptions | false | Optionen wie { once: true }, capture oder signal (AbortSignal) zum Entfernen des Listeners. |
Rückgabewert
addEventListener gibt nichts zurück. Der Handler bekommt ein Event ohne zusätzliche Eigenschaften.Beispiele
Play-Button freischalten, sobald das Video abspielbar ist
const video = document.querySelector('#film');
const playBtn = document.querySelector('#play');
const spinner = document.querySelector('.spinner');
playBtn.disabled = true;
video.addEventListener('canplay', () => {
spinner.hidden = true;
playBtn.disabled = false;
console.log('Bereit, readyState =', video.readyState);
}, { once: true });
playBtn.addEventListener('click', () => video.play());
Autoplay mit Fehlerbehandlung über oncanplay
const audio = new Audio('/media/podcast.mp3');
audio.oncanplay = async () => {
try {
await audio.play();
console.log('Wiedergabe läuft');
} catch (err) {
// Autoplay-Richtlinie blockiert Start ohne Nutzerinteraktion
console.warn('Autoplay blockiert:', err.name);
}
audio.oncanplay = null; // Handler entfernen
};
Ladezeit messen und Listener per AbortController entfernen
const video = document.querySelector('video');
const controller = new AbortController();
const start = performance.now();
video.addEventListener('canplay', () => {
console.log(`Abspielbar nach ${Math.round(performance.now() - start)} ms`);
controller.abort(); // entfernt alle Listener mit diesem Signal
}, { signal: controller.signal });
video.src = '/media/trailer.mp4';
video.load();
Promise-Helfer: auf abspielbaren Zustand warten
const waitForCanPlay = (media) => new Promise((resolve, reject) => {
if (media.readyState >= HTMLMediaElement.HAVE_FUTURE_DATA) return resolve();
media.addEventListener('canplay', () => resolve(), { once: true });
media.addEventListener('error', () => reject(media.error), { once: true });
});
const video = document.querySelector('video');
await waitForCanPlay(video);
console.log('Video kann starten');
// Wichtig · Fallstricke
- Race Condition: Wenn das Medium schon aus dem Cache geladen ist, kann
canplaygefeuert haben, bevor dein Listener registriert wird. Prüfe deshalb vorhermedia.readyState >= 3, wie im Promise-Helfer oben. - Mehrfaches Auslösen: Nach einem Sprung, nach Pufferengpässen oder nach einem Quellenwechsel feuert
canplayerneut. Mit{ once: true }vermeidest du doppelte Initialisierung. preload="none": Dann lädt der Browser keine Daten, undcanplayfeuert erst nachplay()oderload(). Auf Mobilgeräten wirdpreloadoft ignoriert oder eingeschränkt.- Autoplay-Richtlinien: Auch nach
canplaykannplay()mitNotAllowedErrorabgelehnt werden, wenn der Nutzer noch nicht mit der Seite interagiert hat und der Ton nicht stummgeschaltet ist. Das zurückgegebene Promise solltest du immer auswerten. - Kein Aufsteigen im DOM: Das Ereignis bubbelt nicht. Für Delegation brauchst du
capture: true. - Kompatibilität: Wird von allen modernen Browsern unterstützt. Bei
canplaythroughweichen die Browser in ihren Schätzungen stärker voneinander ab.