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

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

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

ดูรายละเอียดทั้งหมดได้ที่เอกสาร 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 แนะนําแนวคิดในการติดตาม "ระดับการเข้าถึง" จริงขององค์ประกอบเป้าหมายตามที่มนุษย์กําหนด เมื่อตั้งค่าตัวเลือกใน IntersectionObserver เครื่องมือสร้าง อินสแตนซ์ IntersectionObserverEntry ที่ซ้อนทับกันจะมีช่องบูลีนใหม่ชื่อ isVisible ค่า true สำหรับ isVisible เป็นการรับประกันอย่างชัดเจนจากการใช้งานพื้นฐานว่าองค์ประกอบเป้าหมายไม่ถูกปิดกั้นโดยเนื้อหาอื่นโดยสิ้นเชิง และไม่มีการใช้เอฟเฟกต์ภาพที่จะเปลี่ยนแปลงหรือทำให้การแสดงผลบนหน้าจอดูบิดเบี้ยว ในทางตรงกันข้าม ค่า false หมายความว่าการติดตั้งใช้งานไม่สามารถรับประกันได้

รายละเอียดที่สำคัญของspecคือ การใช้งานได้รับอนุญาตให้รายงานผลลบลวง (กล่าวคือ การตั้งค่า 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 ของการสาธิต (แต่ตอนนี้ลอง "ดู" วิดีโอลูกสุนัข) อย่าลืมเปิดใช้งาน "โหมด Trick Mode" อีกครั้งเพื่อเปลี่ยนตัวเองเป็นผู้เผยแพร่โฆษณาที่ไม่ซื่อสัตย์ในทันที และดูว่า 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