Pemrosesan Latar Belakang yang Efisien

Fitur web: content-visibility

Jeda tugas latar belakang yang berat saat komponen tidak dirender oleh browser untuk menghemat resource sistem dan masa pakai baterai.

Ringkasan

Properti content-visibility: auto memungkinkan browser melewati penghitungan rendering untuk elemen yang berada jauh di luar area tampilan. Saat browser memutuskan untuk melewati atau melanjutkan rendering untuk suatu elemen, browser akan memicu peristiwa contentvisibilityautostatechange pada elemen tersebut.

Dengan memproses peristiwa ini, Anda dapat menjeda operasi yang berat seperti animasi <canvas>, rendering WebGL, atau polling data WebSocket frekuensi tinggi saat tidak diperlukan, dan melanjutkannya tepat waktu saat browser bersiap menampilkan konten.

contentvisibilityautostatechange vs. IntersectionObserver

Penting untuk memahami kapan harus menggunakan API yang mana:

  • Gunakan IntersectionObserver untuk logika aplikasi yang terkait dengan visibilitas visual yang tepat dari elemen di area pandang (misalnya, data pemuatan lambat, pemicu scroll tanpa batas).
  • Gunakan contentvisibilityautostatechange untuk pekerjaan yang memerlukan banyak rendering (seperti update kanvas yang kompleks atau mutasi DOM yang berat). Peristiwa ini terkait langsung dengan siklus proses rendering internal browser. Browser sering kali mulai merender elemen sebelum elemen tersebut benar-benar muncul di layar (margin pra-render). Peristiwa ini memberi tahu Anda saat hal itu terjadi, sehingga memastikan konten Anda siap dilihat.

Penerapan

1. Menerapkan Visibilitas Konten CSS

Tetapkan content-visibility: auto pada penampung yang berat dan berikan ukuran placeholder untuk mencegah scrollbar melompat.

.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. Memproses Perubahan Status

Tambahkan pemroses peristiwa untuk contentvisibilityautostatechange guna menjeda atau melanjutkan tugas latar belakang.

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 });

Strategi penggantian

Properti content-visibility dan peristiwa contentvisibilityautostatechange terkait adalah progressive enhancement. Di browser yang tidak mendukungnya:

  • Properti CSS diabaikan, dan konten dirender secara normal.
  • Peristiwa ini tidak pernah diaktifkan, sehingga tugas latar belakang akan terus berjalan seperti biasa tanpa pengoptimalan.

Jika Anda harus mendukung jeda tugas di browser lama, Anda dapat beralih menggunakan IntersectionObserver sebagai perkiraan kasar. Hal ini juga membantu menghemat baterai dan CPU di perangkat lama.

// 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);
}