تستهلك عناصر الوسائط (<video> و<audio>) والموارد المرتبطة بها (مثل صور <video poster> والبيانات الوصفية التي تم تحميلها مسبقًا) معدل نقل بيانات كبيرًا للشبكة ومساحة تخزين كبيرة أثناء تحميل الصفحة الأولي. حتى عند ضبط السمة preload="metadata" أو preload="none" على <video>، يجلب المتصفّح صورة 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. وبدون تحديد الحجم بشكل صريح، قد يتعذّر على الفيديو الذي يتم تحميله بشكل بطيء التقاطع مع إطار العرض المرئي ولن يتم تحميله أبدًا، أو قد يتسبب في حدوث تغييرات مزعجة في التصميم (CLS) عند تحميله. - أدرِج السمة
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"على عناصر<video>في الجزء المرئي من الصفحة أو عناصر "سرعة عرض أكبر محتوى مرئي" (LCP)، لأنّ ذلك يؤدي إلى تأخير جلب بيانات كل من الصور والفيديوهات في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')"، يمكنك رصد الميزة 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);
}
}