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

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

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

ดูรายละเอียดทั้งหมดได้ที่ Intersection Observer docs on 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 "คิด" วิดีโอได้รับการคลิกอย่างถูกต้อง มีความโปร่งใส 100% เมื่อคุณคลิก (โดยสมัครใจ)

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

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

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

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

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

โค้ดใหม่มีลักษณะเป็นอย่างไรในทางปฏิบัติ

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

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

ตามสถานการณ์ spec การมองเห็น โดยมีการคำนวณดังนี้

  • หากแอตทริบิวต์ 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