ความน่าเชื่อถือก็ดี แต่การสังเกตก็ดีกว่า: Intersection Observer v2

Intersection Observer v2 เพิ่มความสามารถในการสังเกตไม่เพียงเฉพาะจุดตัดเท่านั้น แต่ยังตรวจจับได้ด้วยว่ามีการแสดงองค์ประกอบที่ตัดกัน ณ เวลาที่มีการตัดกันหรือไม่

Intersection Observer v1 เป็นหนึ่งใน API ที่น่าจะได้รับความนิยมอย่างแพร่หลาย และตอนนี้ที่Safari รองรับด้วย ก็ทำให้ API นี้ใช้งานได้อย่างแพร่หลายในเบราว์เซอร์หลักทุกรุ่นแล้ว หากต้องการทบทวนข้อมูลเกี่ยวกับ API อย่างรวดเร็ว เราขอแนะนําให้ดูเคล็ดลับขนาดเล็กที่มีประสิทธิภาพสูงของ Surma ใน Intersection Observer v1 ที่ฝังไว้ด้านล่าง นอกจากนี้ คุณยังอ่านบทความเชิงลึกของ Surma ได้ด้วย ผู้คนใช้ Intersection Observer v1 สําหรับกรณีการใช้งานที่หลากหลาย เช่น การโหลดรูปภาพและวิดีโอแบบ Lazy Loading, การแจ้งเตือนเมื่อองค์ประกอบถึง position: sticky, การเรียกเหตุการณ์ Analytics และอื่นๆ อีกมากมาย

ดูรายละเอียดทั้งหมดได้ในเอกสาร Intersection Observer ใน MDN แต่เพื่อเป็นการช่วยเตือน หน้าตาของ Intersection Observer 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 v1 มีข้อจํากัดอะไรบ้าง

ขออธิบายให้ชัดเจน Intersection Observer v1 นั้นยอดเยี่ยม แต่ก็ไม่ได้สมบูรณ์แบบ มีบางกรณีที่ API ทำงานไม่ได้ มาดูรายละเอียดกัน Intersection Observer v1 API สามารถบอกได้เมื่อมีการเลื่อนองค์ประกอบไปยังวิวพอร์ตของหน้าต่าง แต่จะไม่บอกคุณว่าองค์ประกอบถูกบดบังโดยเนื้อหาหน้าเว็บอื่นๆ หรือไม่ (นั่นคือเมื่อองค์ประกอบถูกบดบัง) หรือการแสดงผลภาพองค์ประกอบได้รับการแก้ไขโดยเอฟเฟกต์ภาพ เช่น transform, opacity, filter หรือไม่ ซึ่งทำให้องค์ประกอบนั้นมองไม่เห็นได้อย่างมีประสิทธิภาพ

สําหรับองค์ประกอบในเอกสารระดับบนสุด ข้อมูลนี้สามารถระบุได้โดยการวิเคราะห์ DOM ผ่าน JavaScript เช่น ผ่าน DocumentOrShadowRoot.elementFromPoint() แล้วเจาะลึกเพิ่มเติม ในทางตรงกันข้าม คุณจะไม่สามารถรับข้อมูลเดียวกันได้หากองค์ประกอบที่เป็นปัญหาอยู่ใน iframe ของบุคคลที่สาม

เหตุใดการแสดงผลจริงจึงสำคัญมาก

ขออภัยที่อินเทอร์เน็ตเป็นพื้นที่ที่ดึงดูดผู้ไม่ประสงค์ดีที่มีเจตนาร้าย ตัวอย่างเช่น ผู้เผยแพร่โฆษณาที่ไม่น่าไว้วางใจซึ่งแสดงโฆษณาแบบจ่ายต่อคลิกในเว็บไซต์เนื้อหาอาจมีแรงจูงใจที่จะหลอกลวงผู้ใช้ให้คลิกโฆษณาเพื่อเพิ่มการชำระคืนโฆษณาของผู้เผยแพร่โฆษณา (อย่างน้อยก็ในระยะสั้นจนกว่าเครือข่ายโฆษณาจะจับได้) โดยปกติแล้ว โฆษณาดังกล่าวจะแสดงใน iframe หากผู้เผยแพร่โฆษณาต้องการให้ผู้ใช้คลิกโฆษณาดังกล่าว ก็อาจทําให้ iframe ของโฆษณาโปร่งใสได้โดยใช้กฎ CSS iframe { opacity: 0; } และวางซ้อน iframe ไว้บนสิ่งที่น่าสนใจ เช่น วิดีโอแมวน่ารักที่ผู้ใช้อยากคลิกจริงๆ การดำเนินการนี้เรียกว่าคลิกแจ็กกิ้ง คุณสามารถดูการโจมตีด้วยคลิกเจตนาดังกล่าวได้ในส่วนบนของการสาธิตนี้ (ลอง "ดู" วิดีโอแมวและเปิดใช้งาน "โหมดกลโกง") คุณจะเห็นว่าโฆษณาใน iframe "คิดว่า" ได้รับการคลิกที่ถูกต้อง แม้ว่าจะโปร่งแสงโดยสมบูรณ์เมื่อคุณ (แกล้งทำเป็น) คลิกก็ตาม

การหลอกลวงให้ผู้ใช้คลิกโฆษณาโดยจัดรูปแบบให้โฆษณาโปร่งใสและวางซ้อนไว้บนสิ่งที่น่าสนใจ

Intersection Observer v2 แก้ไขปัญหานี้ได้อย่างไร

Intersection Observer v2 นำเสนอแนวคิดในการติดตาม "การแสดงผล" จริงขององค์ประกอบเป้าหมายตามที่มนุษย์จะกำหนด การตั้งค่าตัวเลือกในตัวสร้าง จะทำให้อินสแตนซ์ที่ตัดกันของ IntersectionObserverEntry มีช่องบูลีนใหม่ชื่อ isVisibleIntersectionObserver ค่า true สำหรับ isVisible เป็นการรับประกันที่แน่ชัดจากการใช้งานพื้นฐานว่าองค์ประกอบเป้าหมายไม่ถูกบดบังโดยเนื้อหาอื่นๆ โดยสิ้นเชิง และไม่มีการใช้เอฟเฟกต์ภาพที่จะเปลี่ยนแปลงหรือบิดเบือนการแสดงผลบนหน้าจอ ในทางตรงกันข้าม ค่า false หมายความว่าการติดตั้งใช้งานไม่สามารถรับประกันได้

รายละเอียดที่สําคัญของข้อกําหนดคือการนําไปใช้ได้รับอนุญาตให้รายงานผลลบที่ผิดพลาด (นั่นคือ การตั้งค่า isVisible เป็น false แม้ว่าองค์ประกอบเป้าหมายจะมองเห็นได้ทั้งหมดและไม่มีการแก้ไข) เบราว์เซอร์จะจำกัดการทำงานด้วยกล่องขอบเขตและเรขาคณิตแบบเส้นตรงเพื่อประสิทธิภาพหรือเหตุผลอื่นๆ โดยไม่พยายามทำให้ได้ผลลัพธ์ที่สมบูรณ์แบบแบบพิกเซลสำหรับการแก้ไข เช่น border-radius

อย่างไรก็ตาม ไม่อนุญาตให้ผลบวกลวงเกิดขึ้นไม่ว่าในกรณีใดก็ตาม (นั่นคือ การตั้งค่า isVisible เป็น true เมื่อองค์ประกอบเป้าหมายไม่แสดงอย่างสมบูรณ์และไม่มีการแก้ไข)

โค้ดใหม่มีลักษณะเป็นอย่างไรเมื่อใช้งานจริง

ตอนนี้ตัวสร้าง IntersectionObserver จะรับพร็อพเพอร์ตี้การกําหนดค่าเพิ่มเติม 2 รายการ ได้แก่ delay และ trackVisibility delay คือตัวเลขที่ระบุความล่าช้าขั้นต่ำเป็นมิลลิวินาทีระหว่างการแจ้งเตือนจากผู้สังเกตการณ์สําหรับเป้าหมายหนึ่งๆ trackVisibility คือบูลีนซึ่งระบุว่าผู้สังเกตจะติดตามการเปลี่ยนแปลงระดับการมองเห็นของเป้าหมายหรือไม่

โปรดทราบว่าเมื่อ trackVisibility เป็น true delay ต้องมีค่าอย่างน้อย 100 (นั่นคือ มีการแจ้งเตือนไม่เกิน 1 ครั้งทุกๆ 100 มิลลิวินาที) ดังที่กล่าวไว้ก่อนหน้านี้ การคำนวณการแสดงผลมีค่าใช้จ่ายสูง และข้อกําหนดนี้เป็นการป้องกันการเสื่อมประสิทธิภาพและการใช้แบตเตอรี่ นักพัฒนาแอปที่มีความรับผิดชอบจะใช้ค่าที่ยอมรับได้สูงสุดสำหรับความล่าช้า

ข้อกําหนดปัจจุบันกําหนดให้คํานวณระดับการมองเห็นดังนี้

  • หากแอตทริบิวต์ trackVisibility ของผู้สังเกตการณ์คือ false ระบบจะถือว่าเป้าหมายนั้นมองเห็นได้ ซึ่งสอดคล้องกับลักษณะการทํางานของ v1 ปัจจุบัน

  • หากเป้าหมายมีเมทริกซ์การเปลี่ยนรูปแบบที่มีประสิทธิภาพนอกเหนือจากการเปลี่ยนรูปแบบ 2 มิติหรือการขยายขนาด 2 มิติตามสัดส่วน ระบบจะถือว่าเป้าหมายนั้นมองไม่เห็น

  • หากเป้าหมายหรือองค์ประกอบใดๆ ในเชนบล็อกที่ประกอบด้วยองค์ประกอบนั้นมีความทึบแสงที่มีประสิทธิภาพอื่นนอกเหนือจาก 1.0 ระบบจะถือว่าเป้าหมายนั้นมองไม่เห็น

  • หากเป้าหมายหรือองค์ประกอบใดๆ ในเชนบล็อกที่ประกอบด้วยมีการใช้ตัวกรอง ระบบจะถือว่าเป้าหมายนั้นมองไม่เห็น

  • หากการติดตั้งใช้งานไม่สามารถรับประกันได้ว่าเป้าหมายจะไม่ถูกบดบังโดยเนื้อหาอื่นๆ ของหน้าเว็บเลย ระบบจะถือว่าเป้าหมายนั้นมองไม่เห็น

ซึ่งหมายความว่าการใช้งานในปัจจุบันค่อนข้างอนุรักษ์นิยมกับการรับประกันการแสดงผล เช่น การใช้ฟิลเตอร์โทนสีเทาที่แทบมองไม่เห็น เช่น filter: grayscale(0.01%) หรือการตั้งค่าความโปร่งใสที่แทบมองไม่เห็นด้วย opacity: 0.99 จะทำให้องค์ประกอบนั้นมองไม่เห็น

ด้านล่างนี้คือตัวอย่างโค้ดสั้นๆ ที่แสดงฟีเจอร์ API ใหม่ คุณสามารถดูการทำงานของตรรกะการติดตามการคลิกในส่วนที่ 2 ของเดโม (แต่ตอนนี้ลอง "ดู" วิดีโอลูกสุนัข) อย่าลืมเปิดใช้งาน "โหมดกลโกง" อีกครั้งเพื่อเปลี่ยนตัวเองเป็นผู้เผยแพร่โฆษณาที่ไม่น่าไว้วางใจทันที และดูว่า 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 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'));

ขอขอบคุณ

ขอขอบคุณ Simeon Vincent, Yoav Weiss และ Mathias Bynens ที่ตรวจสอบบทความนี้ รวมถึง Stefan Zager ที่ตรวจสอบและใช้งานฟีเจอร์นี้ใน Chrome รูปภาพหลักโดย Sergey Semin ใน Unsplash