Medienelemente (<video> und <audio>) und die zugehörigen Ressourcen (z. B. <video poster>-Bilder und vorab geladene Metadaten) verbrauchen beim ersten Seitenaufbau erhebliche Netzwerk- und Speicherbandbreite. Selbst wenn preload="metadata" oder preload="none" für ein <video> festgelegt ist, ruft der Browser das poster-Bild sofort ab und preload="metadata" initiiert weiterhin Bereichsanfragen für Medienheader für jeden Player auf der Seite.
Wenn Sie loading="lazy" zu nicht sichtbaren <video>- und <audio>-Elementen hinzufügen, wird der Browser angewiesen, alle Mediennetzwerkanfragen – einschließlich poster-Bilddownloads, preload-Hinweisen und autoplay-Ausführung – aufzuschieben, bis das Element einen vom Browser berechneten Abstand zum visuellen Darstellungsbereich erreicht.
Implementierung
loading="lazy"zu nicht sichtbaren Medien hinzufügen: Wenden Sieloading="lazy"auf<video>- und<audio>-Elemente an, die sich „below the fold“ (mit Scrollen sichtbar) befinden. Lassen Sie das Attributloadingbei Medien im „above the fold“-Bereich weg, damit es standardmäßig aufeagergesetzt wird.- Explizite Werte für
widthundheightfür<video>-Elemente festlegen: Geben Sie für<video>immer ganzzahlige Werte für die Attributewidthundheight(in CSS-Pixeln) an. Nicht geladene Videos haben standardmäßig die Abmessungen300×150. Ohne explizite Größenangabe kann es passieren, dass ein Lazy-Loading-Video nicht in den sichtbaren Darstellungsbereich gelangt und nie geladen wird oder beim Laden störende Layoutverschiebungen (CLS) verursacht. - Attribut
controlsin<audio>-Elementen einfügen: Fügen Siecontrolsimmer in<audio loading="lazy">ein. Da<audio>-Elemente ohne Browsersteuerelemente keine intrinsische visuelle Box haben, kann ein<audio loading="lazy">-Element ohnecontrolsden Darstellungsbereich nicht sichtbar schneiden und wird nie geladen oder automatisch wiedergegeben. - Koordinatenattribute
preloadundposter:- Bei
<video loading="lazy">werden sowohl der Abruf desposter-Bildes als auch das Verhalten vonpreload(none,metadataoderauto) verzögert, bis das Video in den Darstellungsbereich gelangt. - Kombinieren Sie
loading="lazy"mitpreload="none", wenn der Nutzer explizit auf „Wiedergabe“ drücken muss, nachdem er zu den Medien gescrollt hat, oder mitpreload="metadata", wenn die Dauer und die Daten des ersten Frames automatisch ausgefüllt werden sollen, sobald der Player im Darstellungsbereich erscheint.
- Bei
- Sicherer Umgang mit Lazy
autoplay: Wennloading="lazy"mitautoplayfür ein stummgeschaltetes<video>(oder ein Opt-in-<audio controls>) kombiniert wird, werden Wiedergabe und Download erst gestartet, wenn sich das Element dem Darstellungsbereich nähert. Beachte immer@media (prefers-reduced-motion: reduce), bevor du Bewegtbildmedien automatisch abspielst.
Beispielcode
<!-- Above-the-fold hero video: DO NOT use loading="lazy".
Omit loading (defaults to "eager") so the poster and initial media load immediately. -->
<video
controls
width="1280"
height="720"
poster="/images/hero-poster.jpg"
preload="metadata"
>
<source src="/media/keynote.webm" type="video/webm">
<source src="/media/keynote.mp4" type="video/mp4">
<track src="/media/keynote-en.vtt" kind="captions" srclang="en" label="English">
</video>
<!-- Below-the-fold video player:
MANDATORY: Specify integer width and height attributes so the unloaded video
has a non-zero layout box to intersect the viewport and avoids layout shift.
Both the poster image and preload="metadata" are deferred until near the viewport. -->
<video
controls
loading="lazy"
width="800"
height="450"
poster="/images/tutorial-poster.jpg"
preload="metadata"
>
<source src="/media/tutorial.webm" type="video/webm">
<source src="/media/tutorial.mp4" type="video/mp4">
<track src="/media/tutorial-en.vtt" kind="captions" srclang="en" label="English">
</video>
<!-- Below-the-fold inline product loop:
With loading="lazy" and autoplay, downloading and playback wait until the
video approaches the viewport. Include muted and playsinline for autoplay. -->
<video
controls
loading="lazy"
autoplay
loop
muted
playsinline
width="640"
height="360"
poster="/images/feature-loop-poster.jpg"
>
<source src="/media/feature-loop.webm" type="video/webm">
<source src="/media/feature-loop.mp4" type="video/mp4">
<track src="/media/feature-loop-en.vtt" kind="captions" srclang="en" label="English">
</video>
<!-- Below-the-fold audio player:
MANDATORY: Include the controls attribute. Lazy-loaded <audio> without
controls is invisible, cannot intersect the viewport, and will never load. -->
<figure>
<figcaption id="episode-title">Episode 42: Browser Architecture Deep Dive</figcaption>
<audio
controls
loading="lazy"
preload="metadata"
src="/media/episode-42.mp3"
aria-labelledby="episode-title"
aria-details="episode-transcript"
></audio>
<details id="episode-transcript">
<summary>View transcript</summary>
<p>Transcript content for Episode 42...</p>
</details>
</figure>
// Pause autoplaying videos when the user prefers reduced motion
const reducedMotionQuery = window.matchMedia('(prefers-reduced-motion: reduce)');
function syncReducedMotion(prefersReducedMotion) {
for (const video of document.querySelectorAll('video[autoplay]')) {
if (prefersReducedMotion) {
video.removeAttribute('autoplay');
video.pause();
}
}
}
syncReducedMotion(reducedMotionQuery.matches);
reducedMotionQuery.addEventListener('change', (event) => {
syncReducedMotion(event.matches);
});
Best Practices
- OBLIGATORISCH: Legen Sie für
<video>-Elemente immer explizitewidth- undheight-Attribute fest. Nicht geladene Videos haben eine intrinsische Größe von300×150und werden nie geladen, wenn sie sich nicht mit einem sichtbaren Teil der Seite überschneiden. - OBLIGATORISCH: Geben Sie für
<audio loading="lazy">-Elemente immer das Attributcontrolsan. Mit Lazy Loading geladene<audio>-Elemente ohnecontrolswerden nicht sichtbar gerendert und nie geladen oder automatisch abgespielt. - Wende
loading="lazy"NICHT auf „above the fold“- oder Largest Contentful Paint (LCP)-Elemente (<video>) an, da dies das Abrufen vonposter-Bild- und Videodaten verzögert. - Lassen Sie das Attribut
loadingWEG, wenn das standardmäßige Eager Loading gewünscht ist, anstatt explizitloading="eager"zu schreiben. - Verlassen Sie sich NICHT auf das Ereignis
windowload, um zu erkennen, wann per Lazy Loading geladene<video>- oder<audio>-Elemente geladen wurden. Das Ereignisloadwird nur für Medien ausgelöst, die mit Eager Loading geladen werden, auch wenn sich einloading="lazy"-Medienelement anfangs im visuellen Darstellungsbereich befindet. - BEACHTEN SIE, dass Browser
loading="lazy"-Medien nur dann zurückstellen, wenn JavaScript aktiviert ist. Dies ist eine Anti-Tracking-Maßnahme, die verhindert, dass Server die Scrollposition über Medienanfragen ableiten, wenn Scripting deaktiviert ist.
Fallback-Strategie
Wenn Ihr Baseline-Ziel loading="lazy" für <video> und <audio> nicht unterstützt, können Sie zwischen progressiver Verbesserung und einem IntersectionObserver-Fallback wählen, je nachdem, wie streng die Bandbreite eingespart werden muss:
Progressive Verbesserung (empfohlene Standardeinstellung)
Browser, die loading="lazy" für <video> und <audio> nicht erkennen, ignorieren das Attribut und greifen auf das Standardladen zurück, das dem Attribut preload unterliegt. Wenn Sie loading="lazy" mit preload="none" oder preload="metadata" kombinieren, wird verhindert, dass nicht unterstützte Browser die gesamte Mediendatei im Voraus herunterladen. <video poster>-Bilder und preload="metadata"-Header werden jedoch sofort abgerufen.
Fallback für benutzerdefinierten Code (IntersectionObserver)
Wenn Ihr Baseline-Ziel erfordert, dass <video poster>-Downloads, preload="metadata"-Anfragen oder die durch den Darstellungsbereich ausgelöste Wiedergabe in Browsern ohne native Unterstützung strikt verzögert werden, sollten Sie Object.hasOwn(HTMLMediaElement.prototype, 'loading') per Feature-Detection erkennen und dann auf IntersectionObserver zurückgreifen:
// Feature-detect native media lazy loading on HTMLMediaElement.prototype
const supportsLazyMedia = Object.hasOwn(HTMLMediaElement.prototype, 'loading');
if (!supportsLazyMedia) {
const lazyMediaElements = document.querySelectorAll(
'video[loading="lazy"], audio[loading="lazy"][controls]',
);
// Example-only rootMargin value; adjust distance threshold for your layout
const mediaObserver = new IntersectionObserver(
(entries, observer) => {
for (const entry of entries) {
if (!entry.isIntersecting) continue;
const media = entry.target;
// Restore deferred poster and preload attributes when entering the viewport
if (media.dataset.poster) {
media.poster = media.dataset.poster;
delete media.dataset.poster;
}
if (media.dataset.preload) {
media.preload = media.dataset.preload;
delete media.dataset.preload;
}
if (media.dataset.src) {
media.src = media.dataset.src;
delete media.dataset.src;
}
for (const source of media.querySelectorAll('source[data-src]')) {
source.src = source.dataset.src;
delete source.dataset.src;
}
media.load();
observer.unobserve(media);
}
},
{ rootMargin: '200px 0px' },
);
for (const media of lazyMediaElements) {
// Hold back network fetches in unsupported browsers until intersection
if (media.hasAttribute('poster')) {
media.dataset.poster = media.getAttribute('poster');
media.removeAttribute('poster');
}
if (media.hasAttribute('preload') && media.getAttribute('preload') !== 'none') {
media.dataset.preload = media.getAttribute('preload');
}
media.preload = 'none';
mediaObserver.observe(media);
}
}