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, kini Safari juga mendukungnya, akhirnya juga dapat digunakan secara universal di semua {i>browser<i} utama. Untuk mengingat kembali API ini, Saya sarankan untuk menonton video Surma Microtip Supercharged di Persimpangan Observer v1 yang disematkan di bawah. Anda juga dapat membaca secara mendalam Surma artikel ini. Pengguna 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 masih banyak lagi.

Untuk detail selengkapnya, lihat Dokumen Intersection Observer di MDN, tetapi sebagai pengingat singkat, seperti inilah tampilan Intersection Observer v1 API dalam kasus 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 sulit dengan Intersection Observer v1?

Untuk lebih jelasnya, Intersection Observer v1 sangat bagus, tetapi belum sempurna. Ada beberapa kasus sudut yang membuat API gagal. Ayo kita lihat lebih dekat! Intersection Observer v1 API dapat memberi tahu Anda saat elemen di-scroll ke dalam area pandang jendela, namun hal ini tidak memberi tahu Anda apakah elemen tertutup oleh konten halaman lainnya (yaitu, saat elemen terhalang) atau apakah tampilan visual elemen telah dimodifikasi 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() dan kemudian menggali lebih dalam. Sebaliknya, informasi yang sama tidak dapat diperoleh jika elemen yang dimaksud yang berada dalam iframe pihak ketiga.

Mengapa visibilitas aktual dianggap 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 akan diberi insentif untuk mengelabui orang agar mengklik iklannya guna meningkatkan pembayaran iklan penayang (setidaknya untuk sementara waktu, hingga jaringan iklan mendeteksinya). 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 selain dengan sesuatu yang menarik, seperti video kucing lucu yang ingin diklik pengguna. Hal ini disebut clickjacking. Anda dapat melihat serangan {i>clickjacking<i} beraksi di bagian atas halaman ini demo (coba "tonton" video kucing dan aktifkan "mode trik"). Anda akan melihat bahwa iklan dalam iframe "berpikir" iklan tersebut menerima klik yang sah, meskipun sepenuhnya transparan saat Anda (berpura-pura tidak sengaja) mengkliknya.

Memperdaya pengguna agar mengklik iklan dengan memberi gaya transparan dan menghamparkannya di atas sesuatu yang menarik.

Bagaimana cara Intersection Observer v2 memperbaiki masalah ini?

Intersection Observer v2 memperkenalkan konsep pelacakan "visibilitas" aktual target sebagai manusia. Dengan menetapkan opsi di Konstruktor IntersectionObserver, berpotongan IntersectionObserverEntry instance akan berisi kolom boolean baru bernama isVisible. Nilai true untuk isVisible adalah jaminan kuat dari penerapan 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 implementasi tidak dapat memberikan jaminan tersebut.

Detail penting dari spesifikasi adalah 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 dirinya sendiri untuk bekerja dengan pembatas kotak dan geometri garis lurus; mereka tidak mencoba mencapai hasil yang sempurna untuk {i>pixel<i} 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 tersebut dalam praktiknya?

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

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

Menurut laporan spec, visibilitasnya adalah 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 akan dianggap tidak terlihat.

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

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

  • Jika penerapan tidak dapat menjamin bahwa target sepenuhnya tidak diblokir oleh halaman lain maka target dianggap tidak terlihat.

Artinya implementasi saat ini cukup konservatif dengan 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, semuanya akan merender elemen tak terlihat.

Di bawah ini adalah contoh kode singkat yang mengilustrasikan fitur API baru. Anda dapat melihat pelacakan kliknya cara kerja logika di bagian kedua demo (tapi sekarang, coba &quot;tonton&quot; video anak anjingnya). Pastikan untuk mengaktifkan "mode trik" lagi untuk segera mengonversi diri Anda menjadi penayang yang mencurigakan dan lihat bagaimana Intersection Observer v2 mencegah klik iklan yang tidak sah agar tidak 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

Berkat Simeon Vincent, Yoav Weiss dan Mathias Bynens setelah meninjau artikel ini, serta Stefan Zager untuk meninjau dan menerapkan fitur di Chrome. Banner besar oleh Sergey Semin di Unsplash.