Kepercayaan itu baik, pengamatan itu lebih baik: Intersection Observer v2

Browser Support

  • Chrome: 51.
  • Edge: 15.
  • Firefox: 55.
  • Safari: 12.1.

Source

Intersection Observer adalah salah satu API yang mungkin disukai secara universal, dan dapat digunakan di semua browser utama. Developer telah menggunakan API ini untuk berbagai kasus penggunaan, termasuk pemuatan lambat gambar dan video, notifikasi saat elemen mencapai position: sticky, pemicuan peristiwa analisis, dan banyak lagi.

Dalam bentuknya yang paling dasar, inilah tampilan API Intersection Observer v1:

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

Tantangan visibilitas di Intersection Observer v1

Dengan API Intersection Observer v1, Anda dapat mengetahui kapan elemen di-scroll ke area pandang jendela. Namun, Anda tidak dapat menentukan apakah elemen tersebut tertutup oleh konten halaman lain, yang dikenal sebagai oklusi, atau apakah elemen tersebut tampak dimodifikasi oleh CSS, seperti transform, opacity, atau filter, yang dapat membuat elemen tersebut tidak terlihat.

Untuk elemen dalam dokumen tingkat teratas, informasi ini dapat ditentukan dengan menganalisis DOM dengan JavaScript, misalnya dengan DocumentOrShadowRoot.elementFromPoint(). Sebaliknya, informasi yang sama tidak dapat diperoleh jika elemen yang dimaksud berada di iframe pihak ketiga.

Mengapa visibilitas penting?

Sayangnya, internet memiliki pihak yang tidak bertanggung jawab. Misalnya, penayang yang tidak jujur mungkin menggunakan iklan pay per click di situs. Mereka mungkin menipu pengguna agar mengklik iklan ini untuk mendapatkan lebih banyak uang, setidaknya hingga jaringan iklan menemukan skema mereka. Biasanya, iklan tersebut ditayangkan di iframe.

Untuk menipu pengguna, penayang dapat membuat iframe iklan benar-benar transparan dengan CSS: iframe { opacity: 0; }. Kemudian, mereka dapat menempatkan iframe transparan ini di atas konten yang menarik, seperti video kucing lucu, yang ingin diklik pengguna. Tindakan ini disebut clickjacking.

Anda dapat melihat cara kerja serangan clickjacking tersebut di bagian atas demo kami. Coba "tonton" video kucing dan aktifkan mode trik. Iklan di iframe mencatat klik sebagai sah, meskipun Anda (tanpa sengaja) mengkliknya saat iframe transparan.

Menipu pengguna agar mengklik iklan dengan membuat gaya iklan transparan dan menempatkannya di atas sesuatu yang menarik.

Peningkatan pada Intersection Observer v2

Intersection Observer v2 dapat melacak "visibilitas" elemen sebagaimana yang ditentukan oleh manusia. Jika Anda menetapkan opsi di konstruktor IntersectionObserver, instance IntersectionObserverEntry yang dihasilkan menyertakan kolom boolean baru yang disebut isVisible. Jika isVisible adalah true, browser memastikan elemen tidak tertutup sepenuhnya oleh konten lain dan tidak memiliki efek visual yang menyembunyikan atau mengubah tampilannya. Jika isVisible adalah false, browser tidak dapat memberikan jaminan tersebut.

spec memungkinkan negatif palsu: isVisible dapat berupa false meskipun elemen benar-benar terlihat dan tidak berubah. Untuk performa, browser menggunakan penghitungan yang lebih sederhana, seperti kotak pembatas dan bentuk persegi panjang, dan tidak memeriksa setiap piksel untuk detail yang kompleks seperti border-radius.

Namun, positif palsu tidak diizinkan dalam keadaan apa pun. Artinya, isVisible tidak akan menjadi true jika elemen tidak sepenuhnya terlihat dan tidak dimodifikasi.

Terapkan perubahan ini

Konstruktor IntersectionObserver kini menggunakan dua properti konfigurasi tambahan:

  • delay adalah angka yang menunjukkan penundaan minimum dalam milidetik antara notifikasi dari pengamat, untuk target tertentu.
  • trackVisibility adalah boolean untuk menunjukkan apakah observer akan melacak perubahan dalam visibilitas target.

Jika trackVisibility adalah true, delay harus ditetapkan ke 100 atau nilai yang lebih tinggi (yaitu, tidak lebih dari satu notifikasi setiap 100 md). Karena visibilitas mahal untuk dihitung, hal ini merupakan tindakan pencegahan terhadap penurunan performa dan konsumsi baterai. Developer yang bertanggung jawab harus menggunakan nilai penundaan terbesar yang dapat ditoleransi.

spec, menghitung visibilitas. Seperti pada versi 1, saat atribut trackVisibility pengamat adalah false, target dianggap terlihat

Pada versi 2, target dianggap tidak terlihat jika:

  • Memiliki matriks transformasi yang efektif, selain terjemahan 2D atau penskalaan proporsional 2D.

  • Target, atau elemen apa pun dalam rantai blok yang memuatnya, memiliki opasitas efektif yang lebih kecil dari 1,0.

  • Target, atau elemen apa pun dalam rantai blok yang memuatnya, memiliki filter yang diterapkan.

  • Jika penerapan tidak dapat menjamin bahwa target tidak tertutup sepenuhnya oleh konten halaman lain.

Artinya, penerapan saat ini cukup konservatif dalam menjamin visibilitas. Misalnya, menerapkan filter skala abu-abu yang hampir tidak terlihat (filter: grayscale(0.01%)) atau menyetel transparansi terkecil (opacity: 0.99) akan membuat elemen tidak terlihat.

Berikut adalah contoh kode yang mengilustrasikan fitur API baru. Anda dapat melihat logika pelacakan kliknya beraksi di bagian kedua demo. Coba "tonton" video anak. Aktifkan mode penipuan untuk mengubah diri Anda menjadi aktor jahat dan lihat cara Intersection Observer v2 mencegah klik iklan yang tidak sah dilacak. Intersection Observer v2 melindungi kita.

Intersection Observer v2 mencegah klik yang tidak disengaja 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 v2, fallback 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'));

Referensi lainnya

Ucapan terima kasih

Terima kasih kepada Simeon Vincent, Yoav Weiss, dan Mathias Bynens yang telah meninjau, serta Stefan Zager yang telah meninjau dan menerapkan fitur ini di Chrome.