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 artık Safari tarafından da desteklenmektedir. Böylece, nihayet tüm büyük tarayıcılarda evrensel olarak kullanılabilir. API'yi hızlıca tazelemek için Surma'nın aşağıda yerleştirilmiş olan IntersectionObserver v1 ile ilgili Supercharged Microtip (Surma) adlı videosunu izlemenizi öneririz. Surma'nın ayrıntılı makalesini de okuyabilirsiniz. Kullanıcılar, resimlerin ve videoların geç yüklenmesi, öğeler position: sticky tutarına ulaştığında bildirim alma, analiz etkinliklerini tetikleme gibi çok çeşitli kullanım alanları için Intersection Observer v1'i kullandılar.

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'ler içinde sunulur. 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 demonun üst bölümünde bu tür bir tıklama saldırısını iş başında görebilirsiniz (kedi videosunu "izlemeyi" ve "aldatma modunu" 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?

Kesişim Gözlemcisi v2, bir hedef öğenin gerçek "görünürlüğünü" bir insanın tanımlayacağı şekilde izleme kavramını ortaya koyar. 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 olacak?

IntersectionObserver oluşturucusu artık iki ek yapılandırma özelliği alır: delay ve trackVisibility. 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 bir bildirimden fazla olmaması gerektiğini) unutmayın. Daha önce de belirtildiği gibi, görünürlüğün hesaplanması pahalıdır ve bu gereklilik, performansın düşmesine ve pil tüketimine karşı bir önlemdir. Sorumlu geliştirici, gecikme için tolere edilebilir en yüksek değeri kullanır.

Mevcut spesifikasyona göre görünürlük aşağıdaki şekilde 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.

  • Hedef veya bulunduğu blok zincirindeki herhangi bir öğe, 1,0 dışında etkili bir opaklığa sahipse 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 kapatılacağını garanti edemezse hedef görünmez olarak kabul edilir.

Bu, mevcut uygulamaların oldukça ihtiyatlı olduğu ve görünürlük garantisi sağladığı 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 yavru köpek 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

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