โดยปกติแล้ว การซิงค์สถานะ UI กับตำแหน่งสแนปของคอนเทนเนอร์ที่เลื่อนได้ต้องใช้ Listener เหตุการณ์การเลื่อนที่ซับซ้อน การคำนวณออฟเซ็ตการเลื่อนด้วยตนเอง และ Intersection Observer เหตุการณ์ scrollsnapchange เป็นวิธีที่มีประสิทธิภาพและมาพร้อมกับเบราว์เซอร์ในการตรวจจับเมื่อตัวเลื่อนหยุดที่เป้าหมายการสแนปใหม่ ซึ่งมีประโยชน์สำหรับการซิงค์แถบด้านข้างหรือไฮไลต์ส่วนที่ใช้งานอยู่ในสารบัญ
การใช้งาน
1. กำหนดค่าการสแนปการเลื่อนใน CSS
คอนเทนเนอร์ต้องมีการกำหนด scroll-snap-type และมีรายการย่อยที่มี scroll-snap-align เพื่อให้เบราว์เซอร์ติดตามเป้าหมายการสแนปได้ ในบทความยาวๆ ที่มีสารบัญ คุณสามารถใช้ฟีเจอร์นี้เพื่อตรึงส่วนหัวของส่วนต่างๆ ไว้ที่ด้านบนของวิวพอร์ตได้
main {
/* Enable scroll snapping on the container */
scroll-snap-type: y proximity;
overflow-y: auto;
}
h2 {
/* Define how headers align when snapped */
scroll-snap-align: start;
}
2. ฟังการเปลี่ยนแปลงของสแนปชอต
ใช้เหตุการณ์ scrollsnapchange ในคอนเทนเนอร์การเลื่อนเพื่อตอบสนองเมื่อผู้ใช้เลื่อนเสร็จแล้วและเบราว์เซอร์สแนปไปยังองค์ประกอบใหม่ ในการสาธิต TOC เราใช้ฟีเจอร์นี้เพื่อไฮไลต์ลิงก์ที่ใช้งานอยู่ในแถบด้านข้าง
<!-- MANDATORY: Wrap table of contents links inside a proper navigation landmark -->
<nav aria-label="Table of contents">
<ul>
<li><a href="#section-1" aria-current="location">Section 1</a></li>
<li><a href="#section-2">Section 2</a></li>
</ul>
</nav>
const main = document.getElementById('main');
const links = document.querySelectorAll('nav a');
// The event fires when the scroller settles on a new snap target
main.addEventListener('scrollsnapchange', (event) => {
// Use snapTargetBlock for vertical or snapTargetInline for horizontal
const snappedHeader = event.snapTargetBlock;
if (snappedHeader) {
setSelectedParagraph(snappedHeader.id);
}
});
การช่วยเหลือพิเศษ
สำหรับสารบัญ ให้ตรวจสอบว่าลิงก์แถบด้านข้างใช้ aria-current="true" หรือ aria-current="location" เมื่อลิงก์ทำงานอยู่
นอกจากนี้ โปรดระมัดระวังเมื่อใช้ค่า mandatory สำหรับ scroll-snap-type เนื่องจากอาจทำให้เนื้อหาระหว่างจุดสแนปเข้าถึงไม่ได้เมื่อยาวกว่าหน้าจอ
กลยุทธ์สำรอง
หากระบบไม่รองรับ scrollsnapchange ให้ใช้ IntersectionObserver เพื่อตรวจหาองค์ประกอบที่อยู่ด้านบนสุดของแถบเลื่อนในขณะนั้น โปรดทราบว่าลักษณะการทำงานนี้แตกต่างจาก scrollsnapchange เนื่องจากจะทริกเกอร์ขณะที่เกิดการเลื่อน ไม่ใช่เฉพาะเมื่อการเลื่อนหยุดลง
// Feature detect support for scroll snap events
if (!('onscrollsnapchange' in HTMLElement.prototype)) {
const observer = new IntersectionObserver(
() => {
// Each time the set of intersecting headers changes, find the top
// header that is visible.
const topEntry = [...headers].reduce((currentTop, header) => {
// Use the bottom to handle scrolling up, when the top is still offscreen
const {bottom} = header.getBoundingClientRect();
// Don't match if the header's bottom is above the scrollport
if (bottom < 0) return;
if (!currentTop) return header;
return bottom <
currentTop.getBoundingClientRect().bottom
? header
: currentTop;
}, undefined);
if (topEntry) setSelectedParagraph(topEntry.id);
},
{ root: main, threshold: 0.9 // Adjust based on your use case },
);
// Observe all snap targets (e.g., section headers)
document.querySelectorAll('h2').forEach(header => observer.observe(header));
}