Theo cách truyền thống, việc đồng bộ hoá trạng thái giao diện người dùng với vị trí bám dính của vùng chứa có thể cuộn đòi hỏi phải có trình nghe sự kiện cuộn phức tạp, tính toán độ lệch cuộn theo cách thủ công và trình quan sát giao điểm. Sự kiện scrollsnapchange cung cấp một cách hiệu quả và tự nhiên để phát hiện thời điểm trình cuộn đã ổn định trên một mục tiêu đính mới, giúp ích cho việc đồng bộ hoá thanh bên hoặc làm nổi bật phần đang hoạt động trong mục lục.
Triển khai
1. Định cấu hình tính năng Cố định vị trí cuộn trong CSS
Vùng chứa phải được xác định scroll-snap-type và có các phần tử con có scroll-snap-align để trình duyệt theo dõi các mục tiêu đính vào. Trong một bài viết dài có mục lục, bạn có thể sử dụng thuộc tính này để ghim tiêu đề mục vào đầu khung nhìn.
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. Lắng nghe các thay đổi về ảnh chụp nhanh
Sử dụng sự kiện scrollsnapchange trên vùng chứa cuộn để phản ứng khi người dùng cuộn xong và trình duyệt chuyển sang một phần tử mới. Trong bản minh hoạ mục lục, chúng tôi sử dụng thuộc tính này để làm nổi bật đường liên kết đang hoạt động trong thanh bên.
<!-- 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);
}
});
Hỗ trợ tiếp cận
Đối với mục lục, hãy đảm bảo các đường liên kết trên thanh bên sử dụng aria-current="true" hoặc aria-current="location" khi đang hoạt động.
Ngoài ra, hãy cẩn thận khi sử dụng giá trị mandatory cho scroll-snap-type, vì giá trị này có thể khiến nội dung giữa các điểm bám dính không thể truy cập được khi dài hơn màn hình.
Chiến lược dự phòng
Nếu scrollsnapchange không được hỗ trợ, hãy sử dụng IntersectionObserver để phát hiện phần tử nào hiện đang ở đầu trình cuộn. Xin lưu ý rằng hành vi này khác với scrollsnapchange, vì hành vi này sẽ kích hoạt trong khi thao tác cuộn diễn ra, thay vì chỉ khi thao tác cuộn đã ổn định.
// 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));
}