หยุดงานที่ทำงานอยู่เบื้องหลังซึ่งใช้ทรัพยากรมากชั่วคราวเมื่อเบราว์เซอร์ไม่ได้แสดงผลคอมโพเนนต์เพื่อประหยัดทรัพยากรระบบและอายุการใช้งานแบตเตอรี่
ภาพรวม
พร็อพเพอร์ตี้ content-visibility: auto ช่วยให้เบราว์เซอร์ข้ามการคำนวณการแสดงผลสำหรับองค์ประกอบที่อยู่นอกวิวพอร์ตได้ เมื่อเบราว์เซอร์ตัดสินใจข้ามหรือแสดงผลองค์ประกอบต่อ เบราว์เซอร์จะเริ่มเหตุการณ์ contentvisibilityautostatechange ในองค์ประกอบนั้น
การฟังเหตุการณ์นี้จะช่วยให้คุณหยุดการดำเนินการที่ใช้ทรัพยากรมากชั่วคราวได้ เช่น ภาพเคลื่อนไหว <canvas>, การแสดงผล WebGL หรือการโพลข้อมูล WebSocket ความถี่สูงเมื่อไม่จำเป็น และกลับมาดำเนินการต่อได้ทันทีเมื่อเบราว์เซอร์เตรียมแสดงเนื้อหา
contentvisibilityautostatechange เทียบกับ IntersectionObserver
คุณควรทราบว่าควรใช้ API ใดเมื่อใด ดังนี้
- ใช้
IntersectionObserverสำหรับตรรกะของแอปพลิเคชันที่เชื่อมโยงกับการมองเห็นองค์ประกอบในวิวพอร์ต (เช่น ข้อมูลการโหลดแบบ Lazy Loading, ทริกเกอร์การเลื่อนได้ไม่รู้จบ) - ใช้
contentvisibilityautostatechangeสำหรับงานที่ต้องมีการแสดงผลจำนวนมาก (เช่น การอัปเดต Canvas ที่ซับซ้อนหรือการเปลี่ยนแปลง DOM จำนวนมาก) เหตุการณ์นี้เชื่อมโยงโดยตรงกับวงจรการแสดงผลภายในของเบราว์เซอร์ เบราว์เซอร์มักจะเริ่มแสดงผลองค์ประกอบก่อนที่จะปรากฏบนหน้าจอจริง (ระยะขอบก่อนแสดงผล) เหตุการณ์นี้จะบอกคุณเมื่อเกิดเหตุการณ์ดังกล่าวขึ้น เพื่อให้มั่นใจว่าเนื้อหาของคุณพร้อมให้รับชมแล้ว
การใช้งาน
1. ใช้ CSS Content Visibility
ตั้งค่า content-visibility: auto ในคอนเทนเนอร์ที่มีขนาดใหญ่และระบุขนาดตัวยึดตำแหน่งเพื่อป้องกันไม่ให้แถบเลื่อนกระโดด
.heavy-component {
/* Defer rendering work when off-screen */
content-visibility: auto;
/* Mandatory: Provide a placeholder size to prevent layouts shifts.
- 'auto' is optional and enables the browser to remember the actual size
once rendered. It must be paired with a <length> value to be used for
the first render.
- 'none' tells the browser not to apply any intrinsic width to this element.
It can be used for either the height or the width value.
- '500px' is the estimated height of this element. This can be any valid
CSS <length> value. Replace it with the expected height of your
component.
*/
contain-intrinsic-size: auto none auto 500px;
}
2. ฟังการเปลี่ยนแปลงสถานะ
เพิ่ม Listener เหตุการณ์สำหรับ contentvisibilityautostatechange เพื่อหยุดชั่วคราวหรือกลับมาทำงานในเบื้องหลังอีกครั้ง
const component = document.querySelector('.heavy-component');
// Option 1: Direct listener (recommended)
component.addEventListener('contentvisibilityautostatechange', (event) => {
if (event.skipped) {
// The browser skipped rendering this content.
// DO NOT perform heavy mutations or animation loops here.
stopSimulation();
pauseWebSocketPolling();
} else {
// The browser is about to render the content.
// Resume your work so it is ready when visible.
startSimulation();
resumeWebSocketPolling();
}
});
// Option 2: Capturing listener for event delegation
document.addEventListener('contentvisibilityautostatechange', (event) => {
if (event.target.matches('.heavy-component')) {
if (event.skipped) {
stopSimulation();
} else {
startSimulation();
}
}
}, { capture: true });
กลยุทธ์สำรอง
พร็อพเพอร์ตี้ content-visibility และเหตุการณ์ contentvisibilityautostatechange ที่เชื่อมโยงเป็นการเพิ่มประสิทธิภาพแบบต่อเนื่อง ในเบราว์เซอร์ที่ไม่รองรับ ให้ทำดังนี้
- ระบบจะไม่สนใจพร็อพเพอร์ตี้ CSS และแสดงผลเนื้อหาตามปกติ
- เหตุการณ์จะไม่เริ่มทำงาน ดังนั้นงานที่ทำงานอยู่เบื้องหลังจะยังคงทำงานตามปกติโดยไม่มีการเพิ่มประสิทธิภาพ
หากคุณต้องรองรับการหยุดงานชั่วคราวในเบราว์เซอร์รุ่นเก่า คุณสามารถใช้ IntersectionObserver เป็นค่าประมาณคร่าวๆ ได้ ซึ่งจะช่วยประหยัดแบตเตอรี่และ CPU ในอุปกรณ์รุ่นเก่าด้วย
// Fallback using IntersectionObserver for older browsers
const target = document.getElementById('target-container');
// Check if content-visibility is supported
const isSupported = 'contentVisibility' in document.documentElement.style;
if (!isSupported) {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
// The element is close to the screen. Start work!
startSimulation();
} else {
// The element is far away. Pause work!
stopSimulation();
}
});
}, {
// Use rootMargin to start rendering before it hits the screen
rootMargin: '200px'
});
observer.observe(target);
}