IntersectionObservers cho bạn biết khi nào 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 đi vào khung nhìn hiển thị. Bạn nên thực hiện 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ể. Các vấn đề gần như không thể xảy ra khi bạn biết trang web của mình đang được tải bên trong iframe và bạn muốn biết khi nào người dùng có thể thấy 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à một vấn đề phổ biến đối với các quảng cáo, ví dụ như quảng cáo thường xuyên được tải bằng iframe.
Làm cho thử nghiệm về khả năng hiển thị này trở nên hiệu quả hơn là điều IntersectionObserver
được thiết kế cho và được triển khai trên tất cả các trình duyệt hiện đại. IntersectionObserver
cho bạn biết khi một phần tử được quan sát truy cập hoặc thoát khỏi khung nhìn của trình duyệt.
Cách tạo IntersectionObserver
API này khá nhỏ và được mô tả tốt nhất qua một ví dụ sau đây:
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 của bạn sẽ được gọi cả khi phần tử xuất hiện một phần và hoàn toàn rời khỏi khung nhìn.
Nếu cần quan sát nhiều phần tử, bạn có thể 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. Đây là một mảng gồm các đối tượng IntersectionObserverEntry
. Mỗi đối tượng như vậy chứa dữ liệu giao lộ được cập nhật cho một trong các phần tử được quan sát của bạn.
🔽[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, 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 2 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 có thể nhìn thấy được. 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, nhờ thông tin này mà bạn có thể tuỳ ý sử dụng, bạn 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 một cách 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ự còn cho biết rằng các hoạt động triển khai IntersectionObserver
nên sử dụng requestIdleCallback()
. Tức 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ẽ được trình duyệt thực hiện trong thời gian không hoạt động. Đây là một quyết định thiết kế có ý thức.
div cuộn
Tôi không thích thao tác 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 một tuỳ chọn root
cho phép bạn xác định một phương án thay thế cho khung nhìn làm gốc. Điều quan trọng cần lưu ý là root
phải là đối tượng cấp trên của mọi phần tử quan sát được.
Phân giao tất cả!
Không đâu! Nhà phát triển tồi! Bạn chưa lưu tâm đến việc sử dụng chu kỳ CPU của người dùng. Hãy xem xét ví dụ về trình cuộn vô hạn: Trong trường hợp đó, bạn nên thêm sentinels vào DOM và quan sát (và tái chế!) chúng. 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 người gửi đó 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 chúng vào DOM và đặt lại vị trí người gửi cho phù hợp. Nếu tái chế trọng điểm đúng cách, bạn không cần thực hiện thêm lệnh gọi đến observe()
. IntersectionObserver
vẫn hoạt động bình thườ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 và vào 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ẽ cung cấp cho bạn câu trả lời cho câu hỏi: "Phần tử X có đang 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. Thuộc tính 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 của bạn 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á trị này giải thích 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:
Còn lựa chọn nào khác không?
Hiện tại, những lựa chọn nêu trên chỉ còn một lựa chọn khác. rootMargin
cho phép bạn chỉ định lề cho gốc, cho phép bạn tăng hoặc thu nhỏ diện tích dùng cho giao lộ một cách hiệu quả. Các lề này được chỉ định bằng một chuỗi kiểu CSS, á la "10px 20px 30px 40px"
, xác định lề trên, phải, dưới và 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>
kỳ diệu
IntersectionObserver
được thiết kế 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 tiện ích đó có hiển thị trong chế độ xem hay không. Nếu <iframe>
quan sát thấy một trong các phần tử của nó, thì cả việc cuộn <iframe>
lẫn cuộn cửa sổ chứa <iframe>
sẽ kích hoạt lệnh gọi lại vào những thời điểm thích hợp. Tuy nhiên, đối với trường hợp sau, rootBounds
sẽ được đặt thành null
để tránh làm rò rỉ dữ liệu trên nhiều nguồn gốc.
IntersectionObserver
Không có vấn đề gì?
Một điều cần lưu ý là IntersectionObserver
có chủ định không hoàn hảo về pixel cũng như không có độ trễ thấp. Việc sử dụng chúng để triển khai những nỗ lực như ảnh động phụ thuộc vào thao tác cuộn chắc chắn sẽ không thành công vì dữ liệu sẽ bị lỗi thời vào thời điểm bạn sử dụng dữ liệu đó. 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?
Short 'n Sweet: Việc dành quá nhiều thời gian cho lệnh gọi lại sẽ khiến ứng dụng của bạn bị trễ — tất cả các phương pháp phổ biến đều áp dụng.
Đi ra và giao cắt các phần tử của mình
Trình duyệt hỗ trợ IntersectionObserver
rất tốt, vì nó có sẵn trong tất cả cá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ó sẵn 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 đoạn mã polyfill đó mà phương thức triển khai gốc 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.