Gli elementi multimediali (<video> e <audio>) e le risorse associate (come le immagini <video poster> e i metadati precaricati) consumano una larghezza di banda di rete e di archiviazione significativa durante il caricamento iniziale della pagina. Anche quando preload="metadata" o preload="none" è impostato su un <video>, il browser recupera immediatamente l'immagine poster e preload="metadata" avvia comunque le richieste di intervallo per le intestazioni multimediali in ogni player della pagina.
L'aggiunta di loading="lazy" agli elementi <video> e <audio> fuori schermo indica al browser di posticipare tutte le richieste di rete multimediali, inclusi i download di immagini poster, i suggerimenti preload e l'esecuzione di autoplay, finché l'elemento non raggiunge una distanza calcolata dal browser dalla visual viewport.
Come implementare
- Aggiungi
loading="lazy"ai contenuti multimediali fuori schermo: applicaloading="lazy"agli elementi<video>e<audio>posizionati below the fold. Ometti l'attributoloadingsui contenuti multimediali above the fold in modo che il valore predefinito siaeager. - Imposta
widtheheightespliciti sugli elementi<video>: specifica sempre gli attributiwidtheheightinteri (in pixel CSS) su<video>. I video non caricati hanno dimensioni predefinite di300×150; senza un dimensionamento esplicito, un video con caricamento lento potrebbe non intersecare l'area visibile e non caricarsi mai o causare variazioni del layout cumulative (CLS) fastidiose durante il caricamento. - Includi l'attributo
controlsnegli elementi<audio>: includi semprecontrolsin<audio loading="lazy">. Poiché gli elementi<audio>non hanno una casella visiva intrinseca senza i controlli del browser, un elemento<audio loading="lazy">senzacontrolsnon può intersecare visibilmente l'area visibile e non verrà mai caricato o riprodotto automaticamente. - Attributi delle coordinate
preloadeposter:- Il giorno
<video loading="lazy">, sia il recupero dell'immaginepostersia il comportamentopreload(none,metadataoauto) verranno posticipati fino a quando il video non si avvicina all'area visibile. - Associa
loading="lazy"apreload="none"quando l'utente deve premere esplicitamente il pulsante di riproduzione dopo aver fatto scorrere i contenuti multimediali o apreload="metadata"quando i dati sulla durata e sul frame iniziale devono essere compilati automaticamente una volta che il player entra nell'area visibile.
- Il giorno
- Gestisci il caricamento lento di
autoplayin modo sicuro: quandoloading="lazy"viene combinato conautoplaysu un<video>disattivato (o un<audio controls>con attivazione), la riproduzione e il download attendono che l'elemento si avvicini alla viewport. Rispetta sempre@media (prefers-reduced-motion: reduce)prima di riprodurre automaticamente i contenuti multimediali in movimento.
Esempio di codice
<!-- 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 practice
- OBBLIGATORIO: imposta sempre attributi
widtheheightespliciti sugli elementi<video>. I video non caricati hanno una dimensione intrinseca di300×150e non verranno mai caricati se non intersecano una parte visibile della pagina. - OBBLIGATORIO: includi sempre l'attributo
controlsnegli elementi<audio loading="lazy">. Gli elementi<audio>con caricamento lento senzacontrolsnon vengono visualizzati e non verranno mai caricati o riprodotti automaticamente. - NON applicare
loading="lazy"il recupero ritardato<video>agli elementi above the fold o Largest Contentful Paint (LCP), in quanto ritarda il recupero dei dati di immagini e videoposter. - DO: ometti l'attributo
loadingquando vuoi utilizzare il caricamento eager predefinito, anziché scrivere esplicitamenteloading="eager". - NON fare affidamento sull'evento
windowloadper rilevare quando sono stati caricati gli elementi<video>o<audio>con caricamento lento. L'eventoloadviene attivato solo in base ai contenuti multimediali con caricamento eager, anche se un elemento multimedialeloading="lazy"si trova inizialmente all'interno dell'area visibile. - RICORDA che i browser rinviano i contenuti multimediali
loading="lazy"solo quando JavaScript è abilitato (una misura anti-tracciamento per impedire ai server di dedurre la posizione di scorrimento tramite richieste di contenuti multimediali quando lo scripting è disabilitato).
Strategia di riserva
Se il tuo target Baseline non supporta loading="lazy" su <video> e <audio>, scegli tra il potenziamento progressivo e un IntersectionObserver di riserva in base alla rigidità con cui deve essere conservata la larghezza di banda:
Potenziamento progressivo (impostazione predefinita consigliata)
I browser che non riconoscono loading="lazy" su <video> e <audio> ignorano l'attributo e tornano al caricamento standard regolato dall'attributo preload. L'accoppiamento di loading="lazy" con preload="none" o preload="metadata" garantisce che i browser non supportati evitino comunque di scaricare l'intero file multimediale in anticipo, anche se le immagini <video poster> e le intestazioni preload="metadata" verranno recuperate in modo eager.
Codice di riserva personalizzato (IntersectionObserver)
Quando il tuo target Baseline richiede di posticipare rigorosamente i download di <video poster>, le richieste di preload="metadata" o la riproduzione attivata dalla viewport nei browser senza supporto nativo, esegui il rilevamento delle funzionalità di Object.hasOwn(HTMLMediaElement.prototype, 'loading'), quindi esegui il fallback a IntersectionObserver:
// 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);
}
}