Les éléments multimédias (<video> et <audio>) et leurs ressources associées (comme les images <video poster> et les métadonnées préchargées) consomment une bande passante réseau et de stockage importante lors du chargement de page initial. Même lorsque preload="metadata" ou preload="none" est défini sur un <video>, le navigateur récupère immédiatement son image poster, et preload="metadata" lance toujours des requêtes de plage pour les en-têtes multimédias sur chaque lecteur de la page.
L'ajout de loading="lazy" aux éléments <video> et <audio> hors écran indique au navigateur de différer toutes les requêtes réseau multimédias (y compris les téléchargements d'images poster, les indications preload et l'exécution de autoplay) jusqu'à ce que l'élément atteigne une distance calculée par le navigateur par rapport à la fenêtre d'affichage visuelle.
Mise en œuvre
- Ajouter
loading="lazy"aux contenus multimédias hors écran : appliquezloading="lazy"aux éléments<video>et<audio>situés sous la ligne de flottaison. Omettez l'attributloadingsur les contenus multimédias au-dessus de la ligne de flottaison pour qu'il soit défini par défaut sureager. - Définissez explicitement les attributs
widthetheightsur les éléments<video>: spécifiez toujours des attributswidthetheightentiers (en pixels CSS) sur<video>. Les vidéos non chargées ont par défaut des dimensions de300×150. Sans dimensionnement explicite, une vidéo chargée en différé peut ne pas croiser la fenêtre d'affichage visible et ne jamais se charger, ou provoquer des décalages de mise en page (CLS) perturbants lors de son chargement. - Incluez l'attribut
controlsdans les éléments<audio>: incluez toujourscontrolsdans<audio loading="lazy">. Étant donné que les éléments<audio>n'ont pas de zone visuelle intrinsèque sans commandes du navigateur, un élément<audio loading="lazy">sanscontrolsne peut pas croiser visiblement la fenêtre d'affichage et ne se chargera ni ne se lira jamais automatiquement. - Attributs de coordonnées
preloadetposter:- Sur
<video loading="lazy">, la récupération de l'imageposteret le comportementpreload(none,metadataouauto) sont différés jusqu'à ce que la vidéo s'approche de la fenêtre d'affichage. - Associez
loading="lazy"àpreload="none"lorsque l'utilisateur doit appuyer explicitement sur le bouton de lecture après avoir fait défiler l'écran jusqu'au contenu multimédia, ou àpreload="metadata"lorsque les données de durée et d'image initiale doivent être renseignées automatiquement une fois que le lecteur entre dans la fenêtre d'affichage.
- Sur
- Gérer
autoplayde manière sécurisée : lorsqueloading="lazy"est combiné àautoplaysur un élément<video>sans son (ou un élément<audio controls>activé), la lecture et le téléchargement attendent que l'élément s'approche de la fenêtre d'affichage. Respectez toujours@media (prefers-reduced-motion: reduce)avant de lancer la lecture automatique de contenus multimédias animés.
Exemple de code
<!-- 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);
});
Bonnes pratiques
- OBLIGATOIRE : définissez toujours des attributs
widthetheightexplicites sur les éléments<video>. Les vidéos non chargées ont une taille intrinsèque de300×150et ne se chargeront jamais si elles ne croisent pas une partie visible de la page. - OBLIGATOIRE : incluez toujours l'attribut
controlsdans les éléments<audio loading="lazy">. Les éléments<audio>à chargement différé sanscontrolsne sont pas affichés de manière visible et ne seront jamais chargés ni lus automatiquement. - N'appliquez PAS
loading="lazy"aux éléments au-dessus de la ligne de flottaison ou Largest Contentful Paint (LCP)<video>, car cela retarde la récupération des données d'image et de vidéoposter. - À FAIRE : Omettez l'attribut
loadinglorsque le chargement hâtif par défaut est souhaité, plutôt que d'écrire explicitementloading="eager". - NE VOUS FIEZ PAS à l'événement
windowloadpour détecter le chargement des éléments<video>ou<audio>à chargement différé. L'événementloadne se déclenche qu'en fonction des contenus multimédias chargés de manière hâtive, même si un élément multimédialoading="lazy"se trouve initialement dans la fenêtre d'affichage visuelle. - À NOTER : Les navigateurs ne diffèrent les contenus multimédias
loading="lazy"que lorsque JavaScript est activé (mesure anti-suivi pour empêcher les serveurs de déduire la position de défilement via les requêtes multimédias lorsque les scripts sont désactivés).
Stratégie de remplacement
Si votre cible Baseline n'est pas compatible avec loading="lazy" sur <video> et <audio>, choisissez entre l'amélioration progressive et une solution de remplacement IntersectionObserver en fonction de la rigueur avec laquelle la bande passante doit être préservée :
Amélioration progressive (option par défaut recommandée)
Les navigateurs qui ne reconnaissent pas loading="lazy" sur <video> et <audio> ignorent l'attribut et reviennent au chargement standard régi par l'attribut preload. Associer loading="lazy" à preload="none" ou preload="metadata" permet d'éviter que les navigateurs non compatibles téléchargent l'intégralité du fichier multimédia à l'avance, même si les images <video poster> et les en-têtes preload="metadata" sont récupérés de manière hâtive.
Code de remplacement personnalisé (IntersectionObserver)
Lorsque votre cible Baseline nécessite de différer strictement les téléchargements <video poster>, les requêtes preload="metadata" ou la lecture déclenchée par la fenêtre d'affichage dans les navigateurs sans compatibilité native, détectez la fonctionnalité Object.hasOwn(HTMLMediaElement.prototype, 'loading'), puis utilisez 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);
}
}