Элементы мультимедиа (<video> и <audio>) и связанные с ними ресурсы (например, изображения <video poster> и предварительно загруженные метаданные) потребляют значительную пропускную способность сети и хранилища во время начальной загрузки страницы. Даже если для элемента <video> задано значение preload="metadata" или preload="none", браузер сразу же загружает изображение poster, а preload="metadata" по-прежнему инициирует запросы диапазона для заголовков медиафайлов во всех проигрывателях на странице.
Добавление loading="lazy" к элементам <video> и <audio> за пределами экрана указывает браузеру отложить все сетевые запросы медиа, включая загрузку изображений poster, подсказки preload и выполнение autoplay, пока элемент не достигнет рассчитанного браузером расстояния от визуальной области просмотра.
Рекомендации по внедрению
- Добавьте
loading="lazy"к медиаконтенту за пределами экрана. Применитеloading="lazy"к элементам<video>и<audio>, расположенным в [невидимой] нижней части страницы. Не указывайте атрибутloadingдля медиаконтента в верхней части страницы, чтобы по умолчанию использовалось значениеeager. - Явно задавайте атрибуты
widthиheightдля элементов<video>. Всегда указывайте целочисленные значения атрибутовwidthиheight(в пикселях CSS) для элементов<video>. По умолчанию незагруженные видео имеют размеры300×150. Если не указать размеры, видео с отложенной загрузкой может не попасть в видимую область просмотра и не загрузиться или вызвать смещение макета при загрузке. - Добавьте атрибут
controlsв элементы<audio>. Всегда добавляйте атрибутcontrolsв элементы<audio loading="lazy">. Поскольку у элементов<audio>нет собственного визуального поля без элементов управления браузера, элемент<audio loading="lazy">безcontrolsне может пересекаться с областью просмотра и никогда не будет загружен или воспроизведен автоматически. - Атрибуты координат
preloadиposter:- На
<video loading="lazy">получение изображенияposterи поведениеpreload(none,metadataилиauto) откладываются до тех пор, пока видео не приблизится к области просмотра. - Используйте
loading="lazy"сpreload="none", если пользователь должен нажать кнопку воспроизведения после прокрутки до медиаконтента, или сpreload="metadata", если данные о продолжительности и начальном кадре должны заполняться автоматически, когда проигрыватель появляется в области просмотра.
- На
- Безопасная отложенная загрузка
autoplay. Еслиloading="lazy"используется вместе сautoplayв отключенном элементе<video>(или в элементе<audio controls>, для которого включена отложенная загрузка), воспроизведение и скачивание не начнутся, пока элемент не приблизится к области просмотра. Всегда учитывайте значение@media (prefers-reduced-motion: reduce)перед автовоспроизведением медиаконтента.
Пример кода
<!-- 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);
});
Рекомендации
- ОБЯЗАТЕЛЬНО. Всегда задавайте явные атрибуты
widthиheightдля элементов<video>. Незагруженные видео имеют внутренний размер300×150и никогда не загрузятся, если они не пересекаются с видимой частью страницы. - ОБЯЗАТЕЛЬНО. Всегда добавляйте атрибут
controlsв элементы<audio loading="lazy">. Элементы<audio>с отложенной загрузкой безcontrolsне отображаются и не будут загружаться или воспроизводиться автоматически. - НЕ применяйте
loading="lazy"к элементам в верхней части страницы или элементам, связанным с показателем Largest Contentful Paint (LCP)<video>, так как это задерживает получение данных об изображениях и видеоposter. - РЕКОМЕНДУЕТСЯ не указывать атрибут
loading, если вы хотите использовать быструю загрузку по умолчанию, а не задавать ее явным образом с помощью значенияloading="eager". - НЕ полагайтесь на событие
windowload, чтобы определить, когда загрузились элементы<video>или<audio>с отложенной загрузкой. Событиеloadсрабатывает только на основе медиаконтента с немедленной загрузкой, даже если медиаэлементloading="lazy"изначально находится в области просмотра. - УЧИТЫВАЙТЕ, что браузеры откладывают загрузку медиаконтента
loading="lazy", только если включен JavaScript. Это мера защиты от отслеживания, которая не позволяет серверам определять положение прокрутки по запросам медиаконтента, когда скрипты отключены.
Резервная стратегия
Если целевая платформа не поддерживает loading="lazy" в <video> и <audio>, выберите прогрессивное улучшение или резервный вариант IntersectionObserver в зависимости от того, насколько строго нужно экономить пропускную способность.
Прогрессивное улучшение (рекомендуется по умолчанию)
Браузеры, которые не распознают loading="lazy" в <video> и <audio>, игнорируют атрибут и возвращаются к стандартной загрузке, управляемой атрибутом preload. Если вы используете loading="lazy" вместе с preload="none" или preload="metadata", то неподдерживаемые браузеры не будут скачивать весь медиафайл сразу, хотя изображения <video poster> и заголовки preload="metadata" будут извлекаться заранее.
Резервный вариант со специальным кодом (IntersectionObserver)
Если целевая версия Baseline требует строго отложенного скачивания <video poster>, запросов preload="metadata" или воспроизведения, активируемого областью просмотра, в браузерах без встроенной поддержки, определите функцию Object.hasOwn(HTMLMediaElement.prototype, 'loading'), а затем вернитесь к 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);
}
}