Güvenmek iyidir, gözlem daha iyi: Intersection Observer v2

Intersection Observer v2, kesişimleri gözlemlemenin yanı sıra kesişim sırasında kesişen öğenin görünür olup olmadığını algılama özelliğini de ekler.

Intersection Observer v1, muhtemelen herkes tarafından sevilen API'lerden biridir ve Safari de bu API'yi desteklediğine göre artık tüm büyük tarayıcılarda kullanılabilir. API'yi kısaca tekrar öğrenmek için Surma'nın IntersectionObserver v1 ile ilgili aşağıda yerleştirilmiş Süper Yüklü Mikro İpucu videosunu izlemenizi öneririz. Surma'nın ayrıntılı makalesini de okuyabilirsiniz. Kullanıcılar Intersection Observer v1'i resim ve videoların gecikmeli yüklenmesi, öğeler position: sticky'e ulaştığında bildirim alma, analiz etkinlikleri tetikleme ve daha birçok kullanım alanı için kullanmıştır.

Ayrıntılı bilgi için MDN'deki Intersection Observer dokümanlarına göz atın. Intersection Observer v1 API'sinin en temel durumdaki görünümü aşağıdaki gibidir:

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 ile ilgili zorluk nedir?

Intersection Observer v1'in mükemmel olmadığını belirtmek isteriz. API'nin yetersiz kaldığı bazı uç durumlar vardır. Daha yakından inceleyelim. Intersection Observer v1 API, bir öğenin pencerenin görüntü alanının içine ne zaman kaydırıldığını size söyleyebilir ancak öğenin başka bir sayfa içeriği tarafından örtülüp örtülmediğini (yani öğenin engellenip engellenmediğini) veya öğenin görsel görüntüsünün transform, opacity, filter gibi görsel efektlerle değiştirilip değiştirilmediğini bildirmez. Bu da öğeyi etkili bir şekilde görünmez hale getirebilir.

Üst düzey belgedeki bir öğe için bu bilgiler, DOM'u JavaScript aracılığıyla (ör. DocumentOrShadowRoot.elementFromPoint()) analiz ederek ve daha sonra daha ayrıntılı bir inceleme yaparak belirlenebilir. Buna karşılık, söz konusu öğe üçüncü taraf bir iframe'de bulunuyorsa aynı bilgiler elde edilemez.

Gerçek görünürlük neden bu kadar önemli?

İnternet maalesef kötü niyetli kullanıcıları kendine çeken bir yer. Örneğin, bir içerik sitesinde tıklama başına ödemeli reklam yayınlayan şüpheli bir yayıncı, reklam ödemesini artırmak için kullanıcıları reklamlarını tıklamaya teşvik edebilir (en azından reklam ağı onları yakalayana kadar kısa bir süre için). Bu tür reklamlar genellikle iframe'larda yayınlanır. Yayıncı, kullanıcıların bu tür reklamları tıklamasını istiyorsa bir CSS kuralı iframe { opacity: 0; } uygulayarak ve reklam iFrame'lerini kullanıcıların gerçekten tıklamak isteyeceği sevimli bir kedi videosu gibi ilgi çekici bir öğenin üzerine yerleştirerek reklam iFrame'lerini tamamen şeffaf hale getirebilir. Buna tıklama tuzakçılığı denir. Bu tür bir tıklama tuzakçılığı saldırısını bu demo'nun üst kısmında görebilirsiniz (kedi videosunu "izlemeyi" ve "hileli modu" etkinleştirmeyi deneyin). Iframe'deki reklamın, siz (istemeden) tıkladığınızda tamamen şeffaf olsa bile geçerli tıklamalar aldığını "düşündüğünü" fark edeceksiniz.

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

Intersection Observer v2 bu sorunu nasıl düzeltir?

Intersection Observer v2, bir hedef öğenin gerçek "görünürlüğünü" bir insanın tanımlayacağı şekilde izleme kavramını sunar. IntersectionObserver kurucusunda bir seçenek ayarlayarak kesişimdeki IntersectionObserverEntry örnekleri isVisible adlı yeni bir boole alanı içerir. isVisible için true değeri, temel uygulamanın hedef öğenin diğer içerikler tarafından tamamen engellenmediği ve ekrandaki görüntüsünü değiştirecek veya bozacak görsel efektler uygulanmadığı konusunda güçlü bir garantisidir. Buna karşılık, false değeri, uygulamanın bu garantiyi veremeyebileceği anlamına gelir.

Özelliğin önemli bir ayrıntısı, uygulamanın yanlış negatif (yani hedef öğe tamamen görünür ve değiştirilmemiş olsa bile isVisible değerinin false olarak ayarlanması) bildirmesine izin verilmesidir. Tarayıcılar, performans veya başka nedenlerle sınırlayıcı kutular ve doğrusal geometriyle çalışmakla sınırlıdır. border-radius gibi değişiklikler için piksel mükemmelliğinde sonuçlar elde etmeye çalışmazlar.

Bununla birlikte, hiçbir koşulda yanlış pozitiflere izin verilmez (yani hedef öğe tamamen görünür ve değiştirilmemişken isVisible değerinin true olarak ayarlanması).

Yeni kod pratikte nasıl görünüyor?

IntersectionObserver oluşturucusu artık iki ek yapılandırma özelliğini (delay ve trackVisibility) kullanıyor. delay, gözlemciden belirli bir hedef için gelen bildirimler arasındaki minimum gecikmeyi milisaniye cinsinden gösteren bir sayıdır. trackVisibility, gözlemcinin bir hedefin görünürlüğünde yapılan değişiklikleri izleyip izleyemeyeceğini belirten bir boole değeridir.

trackVisibility true olduğunda delay değerinin en az 100 olması gerektiğini (yani 100 ms'de en fazla bir bildirim) unutmayın. Daha önce de belirtildiği gibi, görünürlüğün hesaplanması pahalıdır ve bu şart, performans düşüşü ve pil tüketimine karşı bir önlemdir. Sorumlu geliştirici, gecikme için tolere edilebilir en yüksek değeri kullanır.

Mevcut özelliğe göre görünürlük aşağıdaki gibi hesaplanır:

  • Gözlemcinin trackVisibility özelliği false ise hedefin görünür olduğu kabul edilir. Bu, mevcut v1 davranışına karşılık gelir.

  • Hedefin 2D kaydırma veya orantılı 2D yakınlaştırma dışında etkili bir dönüşüm matrisi varsa hedef görünmez olarak kabul edilir.

  • Hedefin veya içeren blok zincirindeki herhangi bir öğenin etkili opaklığı 1,0'dan farklıysa hedef görünmez olarak kabul edilir.

  • Hedefe veya içeren blok zincirindeki herhangi bir öğeye filtre uygulanmışsa hedef görünmez olarak kabul edilir.

  • Uygulama, hedefin diğer sayfa içerikleri tarafından tamamen engellenmediğini garanti edemiyorsa hedef görünmez olarak kabul edilir.

Bu, mevcut uygulamaların görünürlük garantisi konusunda oldukça muhafazakar olduğu anlamına gelir. Örneğin, filter: grayscale(0.01%) gibi neredeyse fark edilemeyen bir gri tonlama filtresi uygulamak veya opacity: 0.99 ile neredeyse görünmez bir saydamlık ayarlamak, öğeyi görünmez hale getirir.

Aşağıda, yeni API özelliklerini gösteren kısa bir kod örneği verilmiştir. Tıklama izleme mantığının işleyişini demo'nun ikinci bölümünde görebilirsiniz (ancak şimdilik köpek yavrusu videosunu "izlemeyi" deneyin). Kendinizi hemen şüpheli bir yayıncı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 "hileli modu" tekrar etkinleştirdiğinizden emin olun. Bu kez Intersection Observer v2 bize destek oluyor. 🎉

Bir reklamın yanlışlıkla tıklanmasını önleyen Intersection Observer v2.

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

Teşekkür ederiz

Bu makaleyi inceleyen Simeon Vincent, Yoav Weiss ve Mathias Bynens ile Chrome'da özelliği inceleyip uygulayan Stefan Zager'e teşekkür ederiz. Unsplash'taki Sergey Semin'in lokomotif resmi.