信任是好事,觀察是最佳做法:Intersection Observer v2

Browser Support

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

Source

Intersection Observer 可能是普遍受歡迎的 API 之一,而且適用於所有主要瀏覽器。開發人員已將這項 API 用於各種用途,包括延遲載入圖片和影片在元素抵達 position: sticky 時傳送通知觸發 Analytics 事件等等。

最基本的 IntersectionObserver v1 API 如下所示:

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 第 1 版的可見度難題

透過 Intersection Observer v1 API,您可以瞭解元素捲動至視窗檢視區塊的時機。不過,您無法判斷該元素是否遭到其他網頁內容遮蓋 (稱為「遮蔽」),或元素是否因 CSS (例如 transformopacityfilter) 而經過修改,導致元素無法顯示。

如果是頂層文件中的元素,可以透過 JavaScript 分析 DOM 來判斷這項資訊,例如使用 DocumentOrShadowRoot.elementFromPoint()。反之,如果相關元素位於第三方 iframe 中,則無法取得相同資訊。

報表的重要性

遺憾的是,網路上有惡意行為人。舉例來說,不實的發布商可能會在網站上使用單次點擊出價廣告。他們可能會誘騙使用者點擊這些廣告,以賺取更多收益,至少在廣告聯播網發現他們的計畫之前是如此。這類廣告通常會透過 iframe 放送。

為誤導使用者,發布商可能會使用 CSS 將廣告 iframe 完全設為透明:iframe { opacity: 0; }。然後將這些透明的 iframe 放在吸引人的內容上,例如使用者想點選的可愛貓咪影片。這就是所謂的「點擊劫持」

您可以在示範的上方部分,查看這類點擊劫持攻擊的實際運作情形。試著「觀看」貓咪影片,並啟動惡作劇模式。即使您在 iframe 透明時 (無意) 點擊廣告,系統仍會將點擊記錄為有效。

將廣告設為透明,並疊加在吸引人的內容上,誘騙使用者點按廣告。

Intersection Observer 第 2 版的改善項目

Intersection Observer v2 可以追蹤元素「可見度」,就像人類定義的一樣。如果您在 IntersectionObserver 建構函式中設定選項,產生的 IntersectionObserverEntry 執行個體會包含名為 isVisible 的新布林值欄位。當 isVisibletrue 時,瀏覽器會確保元素完全不會被其他內容遮蓋,且沒有任何會隱藏或變更顯示效果的視覺效果。如果 isVisiblefalse,瀏覽器就無法做出這項保證。

根據 規格誤判是允許的:即使元素確實可見且未變更,isVisible 仍可為 false。為了提升效能,瀏覽器會使用較簡單的計算方式 (例如邊界方塊和矩形),不會檢查每個像素的複雜細節 (例如 border-radius)。

但無論如何,我們絕不允許誤報。也就是說,如果元素未完全顯示且未經修改,isVisible 就不會是 true

套用這些變更

IntersectionObserver 建構函式現在會採用兩個額外的設定屬性:

  • delay 是數字,表示特定目標的觀察器通知之間,以毫秒為單位的最短延遲時間。
  • trackVisibility 是布林值,用於指出觀察器是否會追蹤目標顯示設定的變更。

如果 trackVisibilitytruedelay 必須設為 100 或更高的值 (也就是每 100 毫秒最多一則通知)。由於計算可視度需要耗費大量資源,因此這項預防措施可避免效能降低和電池耗電量增加。負責的開發人員應使用可容許的最大延遲值。

規格會計算可視度。與第 1 版相同,當觀察者的 trackVisibility 屬性為 false 時,目標會視為可見

在第 2 版中,如果符合下列條件,目標就會視為隱藏:

  • 具有有效的轉換矩陣,而非 2D 平移或成比例的 2D 放大。

  • 目標或其所含區塊鏈中的任何元素,有效不透明度小於 1.0。

  • 目標或其所含區塊鏈中的任何元素已套用篩選器。

  • 如果導入作業無法保證目標完全不會被其他網頁內容遮住。

這表示目前的實作方式相當保守,以確保曝光度。舉例來說,套用幾乎無法察覺的灰階濾鏡 (filter: grayscale(0.01%)),或設定最小的透明度 (opacity: 0.99),都會導致元素隱形。

以下程式碼範例說明瞭新版 API 的功能。您可以在示範的第二個部分中,查看點擊追蹤邏輯的運作情形。請嘗試「觀看」小狗影片,啟用「詐騙模式」,將自己轉換成惡意行為者,並瞭解 Intersection Observer v2 如何防止系統追蹤非正當的廣告點擊。Intersection Observer v2 可保護我們。

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

其他資源

特別銘謝

感謝 Simeon VincentYoav WeissMathias Bynens 審查,以及 Stefan Zager 審查並在 Chrome 中導入這項功能。