Elemen media (<video> dan <audio>) serta resource terkaitnya (seperti gambar <video poster> dan metadata yang dipramuat) menggunakan bandwidth jaringan dan penyimpanan yang signifikan selama pemuatan halaman awal. Meskipun preload="metadata" atau preload="none" ditetapkan pada <video>, browser akan segera mengambil gambar poster, dan preload="metadata" masih memulai permintaan rentang untuk header media di setiap pemutar di halaman.
Menambahkan loading="lazy" ke elemen <video> dan <audio> di luar layar akan menginstruksikan browser untuk menunda semua permintaan jaringan media—termasuk download gambar poster, petunjuk preload, dan eksekusi autoplay—hingga elemen mencapai jarak yang dihitung browser dari area pandang visual.
Cara menerapkan
- Tambahkan
loading="lazy"ke media di luar layar: Terapkanloading="lazy"ke elemen<video>dan<audio>yang diposisikan di paruh bawah. Hilangkan atributloadingpada media paruh atas sehingga ditetapkan secara default keeager. - Tetapkan
widthdanheighteksplisit pada elemen<video>: Selalu tentukan atributwidthdanheightbilangan bulat (dalam piksel CSS) pada<video>. Video yang tidak dimuat akan ditetapkan ke dimensi300×150secara default; tanpa ukuran eksplisit, video yang dimuat lambat mungkin gagal berpotongan dengan area pandang yang terlihat dan tidak pernah dimuat, atau menyebabkan pergeseran tata letak (CLS) yang mengganggu saat dimuat. - Sertakan atribut
controlspada elemen<audio>: Selalu sertakancontrolspada<audio loading="lazy">. Karena elemen<audio>tidak memiliki kotak visual intrinsik tanpa kontrol browser, elemen<audio loading="lazy">tanpacontrolstidak dapat terlihat berpotongan dengan area tampilan dan tidak akan pernah dimuat atau diputar otomatis. - Atribut koordinat
preloaddanposter:- Di
<video loading="lazy">, pengambilan gambarposterdan perilakupreload(none,metadata, atauauto) ditangguhkan hingga video mendekati area pandang. - Pasangkan
loading="lazy"denganpreload="none"saat pengguna harus secara eksplisit menekan putar setelah men-scroll ke media, ataupreload="metadata"saat durasi dan data frame awal harus diisi secara otomatis setelah pemutar memasuki area pandang.
- Di
- Menangani
autoplaylambat dengan aman: Jikaloading="lazy"digabungkan denganautoplaypada<video>yang dibisukan (atau<audio controls>yang diaktifkan), pemutaran dan download akan menunggu hingga elemen mendekati area pandang. Selalu hormati@media (prefers-reduced-motion: reduce)sebelum memutar otomatis media bergerak.
Kode contoh
<!-- 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);
});
Praktik terbaik
- WAJIB: Selalu tetapkan atribut
widthdanheighteksplisit pada elemen<video>. Video yang tidak dimuat memiliki ukuran intrinsik300×150dan tidak akan pernah dimuat jika tidak berpotongan dengan bagian halaman yang terlihat. - WAJIB: Selalu sertakan atribut
controlspada elemen<audio loading="lazy">. Elemen<audio>yang dimuat lambat tanpacontrolstidak dirender secara terlihat dan tidak akan pernah dimuat atau diputar otomatis. - JANGAN terapkan
loading="lazy"ke elemen paruh atas atau Largest Contentful Paint (LCP)<video>, karena akan menunda pengambilan data gambar dan videoposter. - LAKUKAN penghilangan atribut
loadingjika pemuatan eager default diinginkan, daripada menulisloading="eager"secara eksplisit. - JANGAN mengandalkan peristiwa
windowloaduntuk mendeteksi kapan elemen<video>atau<audio>yang dimuat lambat telah dimuat. Peristiwaloaddiaktifkan hanya berdasarkan media yang dimuat secara eager, meskipun elemen medialoading="lazy"awalnya berada di dalam area pandang visual. - PERHATIKAN bahwa browser hanya menunda media
loading="lazy"jika JavaScript diaktifkan (tindakan antipelacakan untuk mencegah server menyimpulkan posisi scroll melalui permintaan media saat pembuatan skrip dinonaktifkan).
Strategi penggantian
Jika target Dasar Pengukuran Anda tidak mendukung loading="lazy" di <video> dan <audio>, pilih antara progressive enhancement dan penggantian IntersectionObserver berdasarkan seberapa ketat bandwidth harus dihemat:
Progressive enhancement (default yang direkomendasikan)
Browser yang tidak mengenali loading="lazy" di <video> dan <audio> akan mengabaikan atribut tersebut dan kembali ke pemuatan standar yang diatur oleh atribut preload. Menyandingkan loading="lazy" dengan preload="none" atau preload="metadata" memastikan browser yang tidak didukung tetap menghindari download file media lengkap di awal, meskipun gambar <video poster> dan header preload="metadata" akan diambil secara eager.
Penggantian kode kustom (IntersectionObserver)
Jika target Dasar Pengukuran Anda memerlukan penundaan ketat untuk download <video poster>, permintaan preload="metadata", atau pemutaran yang dipicu area pandang di browser tanpa dukungan native, deteksi fitur Object.hasOwn(HTMLMediaElement.prototype, 'loading'), lalu gunakan IntersectionObserver sebagai pengganti:
// 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);
}
}