IntersectionObservers cho bạn biết thời điểm một phần tử được quan sát đi vào hoặc thoát khỏi khung nhìn của trình duyệt.
Giả sử bạn muốn theo dõi thời điểm một phần tử trong DOM của bạn chuyển vào khung nhìn hiển thị. Bạn nên làm việc này để có thể tải từng phần hình ảnh kịp thời hoặc vì bạn cần biết liệu người dùng có thực sự đang xem một biểu ngữ quảng cáo nhất định hay không. Bạn có thể thực hiện việc này bằng cách kết nối sự kiện cuộn hoặc sử dụng bộ tính giờ định kỳ và gọi getBoundingClientRect()
trên phần tử đó.
Tuy nhiên, phương pháp này khá chậm vì mỗi lệnh gọi đến getBoundingClientRect()
buộc trình duyệt phải bố cục lại toàn bộ trang và sẽ khiến trang web của bạn bị giật đáng kể. Mọi việc sẽ trở nên gần như không thể khi bạn biết trang web của mình đang được tải bên trong một iframe và bạn muốn biết thời điểm người dùng có thể xem một phần tử. Mô hình nguồn gốc duy nhất và trình duyệt sẽ không cho phép bạn truy cập bất kỳ dữ liệu nào từ trang web có chứa iframe. Đây là vấn đề thường gặp đối với quảng cáo, chẳng hạn như quảng cáo thường được tải bằng iframe.
IntersectionObserver
được thiết kế để giúp kiểm thử khả năng hiển thị này hiệu quả hơn và đã có mặt trên tất cả trình duyệt hiện đại. IntersectionObserver
cho bạn biết thời điểm một phần tử được quan sát vào hoặc ra khỏi khung nhìn của trình duyệt.
Cách tạo IntersectionObserver
API này khá nhỏ và tốt nhất nên được mô tả bằng ví dụ:
const io = new IntersectionObserver(entries => {
console.log(entries);
}, {
/* Using default options. Details below */
});
// Start observing an element
io.observe(element);
// Stop observing an element
// io.unobserve(element);
// Disable entire IntersectionObserver
// io.disconnect();
Khi sử dụng các tuỳ chọn mặc định cho IntersectionObserver
, lệnh gọi lại sẽ được gọi cả khi phần tử xuất hiện một phần trong khung nhìn và khi phần tử đó rời khỏi khung nhìn hoàn toàn.
Nếu cần quan sát nhiều phần tử, bạn có thể và nên quan sát nhiều phần tử bằng cách sử dụng cùng một thực thể IntersectionObserver
bằng cách gọi observe()
nhiều lần.
Tham số entries
được truyền đến lệnh gọi lại của bạn, là một mảng các đối tượng IntersectionObserverEntry
. Mỗi đối tượng như vậy chứa dữ liệu giao nhau đã cập nhật cho một trong các phần tử được quan sát.
🔽[IntersectionObserverEntry]
time: 3893.92
🔽rootBounds: ClientRect
bottom: 920
height: 1024
left: 0
right: 1024
top: 0
width: 920
🔽boundingClientRect: ClientRect
// ...
🔽intersectionRect: ClientRect
// ...
intersectionRatio: 0.54
🔽target: div#observee
// ...
rootBounds
là kết quả của việc gọi getBoundingClientRect()
trên phần tử gốc, đây là khung nhìn theo mặc định. boundingClientRect
là kết quả của getBoundingClientRect()
được gọi trên phần tử được quan sát. intersectionRect
là giao điểm của hai hình chữ nhật này và cho bạn biết phần nào của phần tử được quan sát là hiển thị. intersectionRatio
có liên quan chặt chẽ và cho bạn biết mức độ hiển thị của phần tử. Giờ đây, bạn có thể tuỳ ý sử dụng thông tin này để có thể triển khai các tính năng như tải tài sản ngay lập tức trước khi chúng xuất hiện trên màn hình. Hiệu quả.
IntersectionObserver
phân phối dữ liệu không đồng bộ và mã gọi lại của bạn sẽ chạy trong luồng chính. Ngoài ra, thông số kỹ thuật thực sự cho biết rằng các hoạt động triển khai IntersectionObserver
phải sử dụng requestIdleCallback()
. Điều này có nghĩa là lệnh gọi đến lệnh gọi lại mà bạn cung cấp có mức độ ưu tiên thấp và sẽ do trình duyệt thực hiện trong thời gian rảnh. Đây là một quyết định thiết kế có ý thức.
Cuộn div
Tôi không thích cuộn bên trong một phần tử, nhưng tôi không phải là người đánh giá và IntersectionObserver
cũng vậy. Đối tượng options
sử dụng tuỳ chọn root
cho phép bạn xác định một đối tượng thay thế cho khung nhìn làm gốc. Điều quan trọng cần lưu ý là root
cần phải là phần tử cấp trên của tất cả các phần tử được quan sát.
Giao nhau tất cả mọi thứ!
Không đâu! Nhà phát triển xấu! Đó không phải là cách sử dụng chu kỳ CPU của người dùng một cách hợp lý. Hãy lấy ví dụ về một thanh cuộn vô hạn: Trong trường hợp đó, bạn nên thêm sentinel vào DOM và quan sát (và tái chế!) các sentinel đó. Bạn nên thêm một người gửi ở gần mục cuối cùng trong trình cuộn vô hạn. Khi trình quan sát đó xuất hiện, bạn có thể sử dụng lệnh gọi lại để tải dữ liệu, tạo các mục tiếp theo, đính kèm các mục đó vào DOM và định vị lại trình quan sát cho phù hợp. Nếu bạn tái chế đúng cách trình cảnh báo, bạn không cần gọi thêm observe()
. IntersectionObserver
tiếp tục hoạt động.
Vui lòng cập nhật thêm
Như đã đề cập trước đó, lệnh gọi lại sẽ được kích hoạt một lần khi phần tử được quan sát xuất hiện một phần trong khung hiển thị và một lần khác khi phần tử đó đã rời khỏi khung nhìn. Bằng cách này, IntersectionObserver
sẽ trả lời câu hỏi "Thành phần X có trong khung hiển thị không?". Tuy nhiên, trong một số trường hợp sử dụng thì mức này có thể là chưa đủ.
Đó là lúc tuỳ chọn threshold
phát huy tác dụng. Phương thức này cho phép bạn xác định một mảng các ngưỡng intersectionRatio
. Lệnh gọi lại sẽ được gọi mỗi khi intersectionRatio
vượt qua một trong các giá trị này. Giá trị mặc định của threshold
là [0]
, giải thích cho hành vi mặc định. Nếu thay đổi threshold
thành [0, 0.25, 0.5, 0.75, 1]
, chúng ta sẽ nhận được thông báo mỗi khi thêm một phần tư của phần tử xuất hiện:
Bạn có lựa chọn nào khác không?
Hiện tại, chỉ có một tuỳ chọn bổ sung ngoài những tuỳ chọn nêu trên. rootMargin
cho phép bạn chỉ định lề cho gốc, cho phép bạn tăng hoặc giảm diện tích dùng cho các giao lộ một cách hiệu quả. Các lề này được chỉ định bằng cách sử dụng một chuỗi kiểu CSS, tương tự như "10px 20px 30px 40px"
, chỉ định lề trên cùng, bên phải, dưới cùng và bên trái tương ứng. Tóm lại, cấu trúc tuỳ chọn IntersectionObserver
cung cấp các tuỳ chọn sau:
new IntersectionObserver(entries => {/* … */}, {
// The root to use for intersection.
// If not provided, use the top-level document's viewport.
root: null,
// Same as margin, can be 1, 2, 3 or 4 components, possibly negative lengths.
// If an explicit root element is specified, components may be percentages of the
// root element size. If no explicit root element is specified, using a
// percentage is an error.
rootMargin: "0px",
// Threshold(s) at which to trigger callback, specified as a ratio, or list of
// ratios, of (visible area / total area) of the observed element (hence all
// entries must be in the range [0, 1]). Callback will be invoked when the
// visible ratio of the observed element crosses a threshold in the list.
threshold: [0],
});
<iframe>
thần kỳ
IntersectionObserver
được thiết kế dành riêng cho các dịch vụ quảng cáo và tiện ích mạng xã hội, thường sử dụng các phần tử <iframe>
và có thể hưởng lợi từ việc biết liệu các phần tử đó có hiển thị hay không. Nếu <iframe>
quan sát một trong các phần tử của nó, thì cả việc cuộn <iframe>
cũng như cuộn cửa sổ chứa <iframe>
sẽ kích hoạt lệnh gọi lại vào thời điểm thích hợp. Tuy nhiên, trong trường hợp sau, rootBounds
sẽ được đặt thành null
để tránh rò rỉ dữ liệu giữa các nguồn gốc.
IntersectionObserver
Không là gì?
Xin lưu ý rằng IntersectionObserver
không cố ý cung cấp độ phân giải pixel hoàn hảo hay độ trễ thấp. Việc sử dụng các lớp này để triển khai các hoạt động như ảnh động phụ thuộc vào thao tác cuộn sẽ không thành công, vì dữ liệu sẽ – nói một cách nghiêm ngặt – đã lỗi thời vào thời điểm bạn sử dụng. Phần giải thích có thêm thông tin chi tiết về các trường hợp sử dụng ban đầu của IntersectionObserver
.
Tôi có thể làm được bao nhiêu việc trong lệnh gọi lại?
Tóm tắt: Việc dành quá nhiều thời gian cho lệnh gọi lại sẽ khiến ứng dụng bị trễ – tất cả các phương pháp thông thường đều áp dụng.
Hãy đi và giao nhau các phần tử của bạn
Trình duyệt hỗ trợ tốt cho IntersectionObserver
, vì trình duyệt này có sẵn trong tất cả trình duyệt hiện đại. Nếu cần, bạn có thể sử dụng polyfill trong các trình duyệt cũ và có trong kho lưu trữ của WICG. Rõ ràng là bạn sẽ không nhận được lợi ích về hiệu suất khi sử dụng polyfill đó mà việc triển khai gốc sẽ mang lại cho bạn.
Bạn có thể bắt đầu sử dụng IntersectionObserver
ngay bây giờ! Hãy cho chúng tôi biết ý tưởng của bạn.