Kepercayaan itu baik, observasi lebih baik: Intersection Observer v2

Intersection Observer v2 menambahkan kemampuan untuk tidak hanya mengamati persimpangan per se, tetapi juga mendeteksi apakah elemen yang berpotongan terlihat pada saat persimpangan.

Intersection Observer v1 adalah salah satu API yang mungkin disukai secara universal, dan, setelah Safari mendukungnya, API ini juga dapat digunakan secara universal di semua browser utama. Untuk mengingat kembali tentang API ini, sebaiknya tonton Supercharged Microtip dari Surma di Intersection Observer v1 yang disematkan di bawah ini. Anda juga dapat membaca artikel surma yang mendalam. Orang-orang telah menggunakan Intersection Observer v1 untuk berbagai kasus penggunaan seperti pemuatan gambar dan video yang lambat, menerima notifikasi saat elemen mencapai position: sticky, mengaktifkan peristiwa analisis, dan banyak lagi.

Untuk mengetahui detail selengkapnya, lihat dokumen Intersection Observer di MDN, tetapi sebagai pengingat singkat, seperti inilah tampilan Intersection Observer v1 API dalam kasus yang paling dasar:

const onIntersection = (entries) => {
  for (const entry of entries) {
    if (entry.isIntersecting) {
      console.log(entry);
    }
  }
};

const observer = new IntersectionObserver(onIntersection);
observer.observe(document.querySelector('#some-target'));

Apa yang menantang dengan Intersection Observer v1?

Untuk lebih jelasnya, Intersection Observer v1 bagus, tetapi belum sempurna. Ada beberapa kasus yang tidak tepat saat API tersebut gagal. Mari kita lihat lebih dekat! Intersection Observer v1 API dapat memberi tahu Anda saat elemen di-scroll ke area pandang jendela, tetapi tidak memberi tahu Anda apakah elemen tertutup oleh konten halaman lainnya (yaitu, saat elemen terhalang) atau apakah tampilan visual elemen telah diubah oleh efek visual seperti transform, opacity, filter, dll., yang secara efektif dapat membuatnya tidak terlihat.

Untuk elemen dalam dokumen tingkat atas, informasi ini dapat ditentukan dengan menganalisis DOM melalui JavaScript, misalnya melalui DocumentOrShadowRoot.elementFromPoint() lalu mempelajarinya lebih dalam. Sebaliknya, informasi yang sama tidak dapat diperoleh jika elemen yang dipermasalahkan berada di iframe pihak ketiga.

Mengapa visibilitas aktual sangatlah penting?

Sayangnya, internet adalah tempat yang menarik pihak tidak bertanggung jawab dengan niat buruk. Misalnya, penayang mencurigakan yang menayangkan iklan bayar per klik di situs konten mungkin diberi insentif untuk mengelabui orang agar mengklik iklan mereka guna meningkatkan pembayaran iklan penayang (setidaknya untuk jangka waktu yang singkat, sampai jaringan iklan mendapatkannya). Biasanya, iklan semacam ini ditayangkan dalam iframe. Kini, jika penayang ingin membuat pengguna mengklik iklan tersebut, mereka dapat membuat iframe iklan sepenuhnya transparan dengan menerapkan aturan CSS iframe { opacity: 0; } dan menempatkan iframe di atas sesuatu yang menarik, seperti video kucing lucu yang sebenarnya ingin diklik pengguna. Tindakan ini disebut clickjacking. Anda dapat melihat penerapan serangan clickjacking semacam itu di bagian atas demo ini (coba "tonton" video kucing dan aktifkan "mode trick"). Anda akan melihat bahwa iklan di iframe "berpikir" bahwa iklan tersebut menerima klik yang sah, meskipun iklan tersebut benar-benar transparan saat Anda (berpura-pura tanpa sadar) mengkliknya.

Menipu pengguna untuk mengklik iklan dengan memberi gaya transparan dan menempatkan iklan di atas sesuatu yang menarik.

Bagaimana Intersection Observer v2 memperbaikinya?

Intersection Observer v2 memperkenalkan konsep pelacakan "visibilitas" sebenarnya dari elemen target seperti yang ditentukan oleh manusia. Dengan menetapkan opsi di konstruktor IntersectionObserver, instance IntersectionObserverEntry yang berpotongan kemudian akan berisi kolom boolean baru bernama isVisible. Nilai true untuk isVisible merupakan jaminan kuat dari implementasi yang mendasarinya bahwa elemen target benar-benar tidak terhalang oleh konten lain dan tidak menerapkan efek visual yang akan mengubah atau mendistorsi tampilannya di layar. Sebaliknya, nilai false berarti bahwa implementasi tidak dapat membuat jaminan tersebut.

Detail penting dari spec ini adalah bahwa penerapan tersebut diizinkan untuk melaporkan negatif palsu (yaitu, menetapkan isVisible ke false bahkan saat elemen target sepenuhnya terlihat dan tidak diubah). Karena performa atau alasan lainnya, browser membatasi diri pada penggunaan kotak pembatas dan geometri garis lurus; browser tidak mencoba mencapai hasil yang sempurna untuk piksel untuk modifikasi seperti border-radius.

Meskipun demikian, positif palsu tidak diizinkan dalam keadaan apa pun (yaitu, menetapkan isVisible ke true saat elemen target tidak sepenuhnya terlihat dan tidak diubah).

Seperti apa tampilan kode baru ini dalam praktiknya?

Konstruktor IntersectionObserver kini menggunakan dua properti konfigurasi tambahan: delay dan trackVisibility. delay adalah angka yang menunjukkan penundaan minimum dalam milidetik antar-notifikasi dari observer untuk target tertentu. trackVisibility adalah boolean yang menunjukkan apakah observer akan melacak perubahan dalam visibilitas target.

Penting untuk diperhatikan di sini bahwa jika trackVisibility adalah true, delay harus setidaknya 100 (yaitu, tidak lebih dari satu notifikasi setiap 100 md). Seperti disebutkan sebelumnya, visibilitas mahal untuk dihitung, dan persyaratan ini merupakan tindakan pencegahan terhadap penurunan performa dan konsumsi baterai. Developer yang bertanggung jawab akan menggunakan nilai terbesar yang dapat ditoleransi untuk penundaan.

Menurut spec saat ini, visibilitas dihitung sebagai berikut:

  • Jika atribut trackVisibility observer adalah false, target dianggap terlihat. Hal ini sesuai dengan perilaku v1 saat ini.

  • Jika target memiliki matriks transformasi yang efektif selain terjemahan 2D atau peningkatan skala 2D proporsional, target tersebut dianggap tidak terlihat.

  • Jika target, atau elemen apa pun dalam rantai blok yang memuatnya, memiliki opasitas efektif selain 1,0, maka target dianggap tidak terlihat.

  • Jika target, atau elemen apa pun dalam rantai blok yang memuatnya, menerapkan filter, target dianggap tidak terlihat.

  • Jika implementasinya tidak dapat menjamin bahwa target benar-benar tidak terhalang oleh konten halaman lain, target dianggap tidak terlihat.

Ini berarti implementasi saat ini cukup konservatif untuk menjamin visibilitas. Misalnya, menerapkan filter hitam putih yang hampir tidak terlihat seperti filter: grayscale(0.01%) atau menyetel transparansi yang hampir tidak terlihat dengan opacity: 0.99 akan membuat elemen menjadi tidak terlihat.

Berikut adalah contoh kode singkat yang mengilustrasikan fitur-fitur API baru. Anda dapat melihat cara kerja logika pelacakan klik di bagian kedua demo ini (tetapi sekarang, coba "menonton" video tentang anak tersebut). Pastikan untuk mengaktifkan "mode trik" lagi untuk langsung mengonversi diri Anda menjadi penayang yang mencurigakan, dan lihat cara Intersection Observer v2 mencegah pelacakan klik iklan yang tidak sah. Kali ini, Intersection Observer v2 siap membantu! 🎉

Intersection Observer v2 mencegah klik yang tidak diinginkan pada iklan.

<!DOCTYPE html>
<!-- This is the ad running in the iframe -->
<button id="callToActionButton">Buy now!</button>
// This is code running in the iframe.

// The iframe must be visible for at least 800ms prior to an input event
// for the input event to be considered valid.
const minimumVisibleDuration = 800;

// Keep track of when the button transitioned to a visible state.
let visibleSince = 0;

const button = document.querySelector('#callToActionButton');
button.addEventListener('click', (event) => {
  if ((visibleSince > 0) &&
      (performance.now() - visibleSince >= minimumVisibleDuration)) {
    trackAdClick();
  } else {
    rejectAdClick();
  }
});

const observer = new IntersectionObserver((changes) => {
  for (const change of changes) {
    // ⚠️ Feature detection
    if (typeof change.isVisible === 'undefined') {
      // The browser doesn't support Intersection Observer v2, falling back to v1 behavior.
      change.isVisible = true;
    }
    if (change.isIntersecting && change.isVisible) {
      visibleSince = change.time;
    } else {
      visibleSince = 0;
    }
  }
}, {
  threshold: [1.0],
  // 🆕 Track the actual visibility of the element
  trackVisibility: true,
  // 🆕 Set a minimum delay between notifications
  delay: 100
}));

// Require that the entire iframe be visible.
observer.observe(document.querySelector('#ad'));

Ucapan terima kasih

Terima kasih kepada Simeon Vincent, Yoav Weiss, dan Mathias Bynens yang telah meninjau artikel ini, dan juga Stefan Zager karena telah meninjau dan menerapkan fitur tersebut di Chrome. Banner besar oleh Sergey Semin di Unsplash.