Kepercayaan itu baik, observasi lebih baik: Intersection Observer v2

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

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

Untuk 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 tantangan Intersection Observer v1?

Agar jelas, Intersection Observer v1 sangat bagus, tetapi tidak sempurna. Ada beberapa kasus ekstrem yang tidak dapat ditangani API. Ayo kita lihat lebih dekat! Intersection Observer v1 API dapat memberi tahu Anda saat elemen di-scroll ke dalam area pandang jendela, tetapi tidak memberi tahu Anda apakah elemen tersebut tertutup oleh konten halaman lain (yaitu, saat elemen dioklusi) 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 teratas, informasi ini dapat ditentukan dengan menganalisis DOM melalui JavaScript, misalnya melalui DocumentOrShadowRoot.elementFromPoint(), lalu mempelajari lebih lanjut. Sebaliknya, informasi yang sama tidak dapat diperoleh jika elemen yang dimaksud terletak di iframe pihak ketiga.

Mengapa visibilitas sebenarnya sangat penting?

Sayangnya, Internet adalah tempat yang menarik pelaku kejahatan dengan niat yang lebih buruk. Misalnya, penayang tidak tepercaya 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 singkat, hingga jaringan iklan menangkapnya). Biasanya, iklan tersebut ditayangkan dalam iframe. Sekarang, jika penayang ingin 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 benar-benar ingin diklik pengguna. Hal ini disebut clickjacking. Anda dapat melihat cara kerja serangan clickjacking seperti itu di bagian atas demo ini (coba "tonton" video kucing dan aktifkan "mode trik"). Anda akan melihat bahwa iklan di iframe "berpikir" bahwa iklan tersebut menerima klik yang sah, meskipun iklan tersebut benar-benar transparan saat Anda (pura-pura tidak sengaja) mengkliknya.

Menipu pengguna agar mengklik iklan dengan menata gayanya agar transparan dan menempatkannya di atas sesuatu yang menarik.

Bagaimana cara Intersection Observer v2 memperbaikinya?

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

Detail penting dari spesifikasi adalah bahwa penerapan diizinkan untuk melaporkan negatif palsu (yaitu, menetapkan isVisible ke false meskipun elemen target benar-benar terlihat dan tidak diubah). Untuk performa atau alasan lainnya, browser membatasi diri untuk bekerja dengan kotak batas dan geometri garis lurus; browser tidak mencoba mencapai hasil yang sempurna 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 kode baru dalam praktiknya?

Konstruktor IntersectionObserver kini menggunakan dua properti konfigurasi tambahan: delay dan trackVisibility. delay adalah angka yang menunjukkan penundaan minimum dalam milidetik antara notifikasi dari pengamat untuk target tertentu. trackVisibility adalah boolean yang menunjukkan apakah observer akan melacak perubahan 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 yang telah disebutkan sebelumnya, visibilitas mahal untuk dihitung, dan persyaratan ini adalah tindakan pencegahan terhadap degradasi performa dan konsumsi baterai. Developer yang bertanggung jawab akan menggunakan nilai terbesar yang dapat ditoleransi untuk penundaan.

Menurut spesifikasi saat ini, visibilitas dihitung sebagai berikut:

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

  • Jika target memiliki matriks transformasi yang efektif selain terjemahan 2D atau penskalaan 2D proporsional, target akan dianggap tidak terlihat.

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

  • Jika target, atau elemen apa pun dalam blockchain yang berisinya, memiliki filter yang diterapkan, target tersebut dianggap tidak terlihat.

  • Jika implementasi tersebut tidak dapat menjamin bahwa target benar-benar tidak terhalang oleh konten halaman lainnya, target tersebut akan dianggap tidak terlihat.

Artinya, implementasi saat ini cukup konservatif dalam 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 tidak terlihat.

Di bawah ini adalah contoh kode singkat yang mengilustrasikan fitur API baru. Anda dapat melihat logika pelacakan kliknya beroperasi di bagian kedua demo (tetapi sekarang, coba "tonton" video anjing). Pastikan untuk mengaktifkan "mode trik" lagi untuk segera mengubah diri Anda menjadi penayang yang tidak jujur dan melihat cara Intersection Observer v2 mencegah klik iklan yang tidak sah dilacak. Kali ini, Intersection Observer v2 kembali hadir! 🎉

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, serta Stefan Zager yang juga telah meninjau dan menerapkan fitur tersebut di Chrome. Gambar hero oleh Sergey Semin di Unsplash.