Güven iyidir, gözlem en iyisidir: Intersection Observer v2

Browser Support

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

Source

Intersection Observer, muhtemelen herkesin sevdiği ve tüm büyük tarayıcılarda kullanılabilen API'lerden biridir. Geliştiriciler bu API'yi resim ve videoları geç yükleme, öğeler position: sticky'ye ulaştığında bildirim gönderme, analiz etkinliklerini tetikleme gibi çok çeşitli kullanım alanlarında kullanmıştır.

En temel haliyle IntersectionObserver v1 API şu şekilde görünür:

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

Intersection Observer v1'deki görünürlük sorunları

Intersection Observer v1 API ile bir öğenin pencerenin görünüm alanına ne zaman kaydırıldığını öğrenebilirsiniz. Ancak bu öğenin kapatma olarak bilinen diğer sayfa içeriği tarafından kapsanıp kapsanmadığını veya öğenin CSS tarafından değiştirilmiş görünüp görünmediğini (ör. transform, opacity veya filter) belirleyemezsiniz. Bu durum, öğeyi görünmez hale getirebilir.

Üst düzey dokümandaki bir öğe için bu bilgiler, DOM'un JavaScript ile analiz edilmesiyle (ör. DocumentOrShadowRoot.elementFromPoint() ile) belirlenebilir. Buna karşılık, söz konusu öğe üçüncü taraf iframe'inde bulunuyorsa aynı bilgiler elde edilemez.

Görünürlük neden önemlidir?

Maalesef internette kötü niyetli kişiler var. Örneğin, dürüst olmayan bir yayıncı, web sitesinde tıklama başına ödeme reklamları kullanabilir. Bu kişiler, reklam ağı planlarını keşfedene kadar en azından daha fazla para kazanmak için kullanıcıları bu reklamları tıklamaya ikna edebilir. Genellikle bu tür reklamlar iFrame'lerde yayınlanır.

Yayıncı, kullanıcıları kandırmak için reklam iFrame'lerini CSS ile tamamen şeffaf hale getirebilir: iframe { opacity: 0; }. Ardından, bu şeffaf iFrame'leri, kullanıcıların tıklamak isteyeceği sevimli bir kedi videosu gibi ilgi çekici içeriklerin üzerine yerleştirebilirler. Buna tıklama hırsızlığı denir.

Bu tür bir tıklama korsanlığı saldırısının nasıl gerçekleştiğini demomuzun üst bölümünde görebilirsiniz. Kedi videosunu "izlemeyi" deneyin ve hile modunu etkinleştirin. iFrame'deki reklam, iFrame şeffafken (istemeden) tıklamış olsanız bile tıklamaları geçerli olarak kaydeder.

Reklamı şeffaf şekilde tasarlayıp çekici bir öğenin üzerine yerleştirerek kullanıcıyı reklamı tıklamaya yönlendirme.

Intersection Observer v2'deki iyileştirmeler

Intersection Observer v2, bir öğenin "görünürlüğünü" bir insanın tanımlayacağı şekilde izleyebilir. IntersectionObserver yapıcısında bir seçenek ayarlarsanız ortaya çıkan IntersectionObserverEntry örnekleri, isVisible adlı yeni bir boole alanı içerir. isVisible true olduğunda tarayıcı, öğenin diğer içeriklerle tamamen kaplanmadığından ve görünümünü gizleyen veya değiştiren görsel efektler içermediğinden emin olur. isVisible false ise tarayıcı bu garantiyi veremez.

Spec, yanlış negatiflere izin verir: Öğeler gerçekten görünür ve değişmemiş olsa bile isVisible olabilir.false Performans için tarayıcılar, sınırlayıcı kutular ve dikdörtgen şekiller gibi daha basit hesaplamalar kullanır ve her pikseli border-radius gibi karmaşık ayrıntılar açısından kontrol etmez.

Ancak hiçbir durumda yanlış pozitiflere izin verilmez. Bu, öğe tamamen görünür ve değiştirilmemişse isVisible değerinin true olmayacağı anlamına gelir.

Bu değişiklikleri uygula

IntersectionObserver oluşturucusu artık iki ek yapılandırma özelliği alıyor:

  • delay, belirli bir hedef için gözlemciden gelen bildirimler arasındaki milisaniye cinsinden minimum gecikmeyi gösteren bir sayıdır.
  • trackVisibility, gözlemcinin bir hedefin görünürlüğündeki değişiklikleri izleyip izlemeyeceğini belirten bir Boole değeridir.

trackVisibility değeri true olduğunda delay, 100 veya daha yüksek bir değere ayarlanmalıdır (yani her 100 ms'de birden fazla bildirim gönderilmemelidir). Görünürlüğün hesaplanması maliyetli olduğundan bu, performans düşüşü ve pil tüketimine karşı bir önlemdir. Sorumlu geliştiriciler, gecikme için tolere edilebilir en büyük değeri kullanmalıdır.

Şartname, görünürlüğü hesaplar. 1. sürümde olduğu gibi, gözlemcinin trackVisibility özelliği false olduğunda hedef görünür kabul edilir.

2. sürümde hedef şu durumlarda görünmez kabul edilir:

  • 2D çeviri veya orantılı 2D ölçek büyütme dışında etkili bir dönüşüm matrisi içeriyor.

  • Hedefin veya onu içeren blok zincirindeki herhangi bir öğenin opaklığı 1,0'dan küçük.

  • Hedef veya hedefi içeren blok zincirindeki herhangi bir öğeye filtre uygulanmışsa

  • Uygulama, hedefin diğer sayfa içerikleri tarafından tamamen engellenmeyeceğini garanti edemiyorsa.

Bu nedenle, mevcut uygulamalar görünürlüğü garanti etme konusunda oldukça muhafazakardır. Örneğin, neredeyse fark edilmeyen bir gri tonlama filtresi uygulama (filter: grayscale(0.01%)) veya en küçük şeffaflığı ayarlama (opacity: 0.99) öğeyi görünmez hale getirir.

Aşağıda, yeni API özelliklerini gösteren bir kod örneği verilmiştir. Tıklama izleme mantığını demonun ikinci bölümünde görebilirsiniz. Köpek yavrusu videosunu "izlemeyi" deneyin. Kendinizi kötü niyetli bir kullanıcıya dönüştürmek ve Intersection Observer v2'nin meşru olmayan reklam tıklamalarının izlenmesini nasıl engellediğini görmek için hızlı ileri/geri modunu etkinleştirin. Intersection Observer v2 bizi korur.

Intersection Observer v2, bir reklamın yanlışlıkla tıklanmasını önler.

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

Ek kaynaklar

Teşekkür

İnceleme için Simeon Vincent, Yoav Weiss ve Mathias Bynens'e, özelliği inceleyip Chrome'da uyguladığı için Stefan Zager'e teşekkür ederiz.