IntersectionObservers cho bạn biết thời điểm một phần tử được quan sát xuất hiện 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 xuất hiện trong khung nhìn. Bạn có thể muốn làm việc này để có thể tải chậm hình ảnh đúng lúc 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 bằng cách sử dụng bộ hẹn giờ định kỳ và gọi getBoundingClientRect() trên phần tử đó.
Tuy nhiên, phương pháp này cực kỳ 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ẽ gây ra hiện tượng giật đáng kể cho trang web của bạn. Mọi việc gần như không thể thực hiện được 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 khi nào người dùng có thể nhìn 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 vào bất kỳ dữ liệu nào từ trang web 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 thử nghiệm 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 khi một phần tử được quan sát xuất hiện 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ả rõ nhất bằng một 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 của bạn 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, đây 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 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, theo mặc định là khung nhìn. 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ử. Với thông tin này, giờ đây bạn có thể triển khai các tính năng như tải thành phần đúng lúc trước khi chúng xuất hiện trên màn hình. Hiệu quả.
IntersectionObserver cung cấp 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 phương thức triển khai IntersectionObserver nên 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ẽ được trình duyệt thực hiện trong thời gian rảnh. Đây là một quyết định có chủ đích khi thiết kế.
Thẻ div có thể cuộn
Tôi không thích cuộn bên trong một phần tử, nhưng tôi không ở đây để phán xét và IntersectionObserver cũng vậy. Đối tượng options lấy tuỳ chọn root cho phép bạn xác định một lựa chọn thay thế cho khung nhìn làm gốc. Bạn cần lưu ý rằng root phải là thành phần gốc của tất cả các thành phần được quan sát.
Giao nhau tất cả mọi thứ!
Không! Nhà phát triển tồi! Điều này cho thấy bạn không chú ý đến việc sử dụng chu kỳ CPU của người dùng. Hãy xem xét một ví dụ về trình cuộn vô hạn: Trong trường hợp đó, bạn chắc chắn nên thêm sentinels vào DOM và quan sát (và tái chế!) các phần tử đó. Bạn nên thêm một lính gác gần mục cuối cùng trong trình cuộn vô hạn. Khi sentinel đó 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à đặt lại vị trí của sentinel cho phù hợp. Nếu bạn tái chế đúng cách giá trị lính canh, thì không cần gọi thêm observe(). IntersectionObserver vẫn tiếp tục hoạt động.
Vui lòng cung cấp thêm thông tin cập nhật
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 nhìn 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ẽ cho bạn câu trả lời cho câu hỏi "Phần tử X có xuất hiện trong khung nhìn không?". Tuy nhiên, trong một số trường hợp sử dụng, điều đó có thể là chưa đủ.
Đây là lúc bạn cần dùng đến lựa chọn threshold. Công cụ này cho phép bạn xác định một mảng gồm 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ả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 có thêm một phần tư phần tử hiển thị:
Còn lựa chọn nào khác không?
Hiện tại, chỉ có một lựa chọn bổ sung cho các lựa chọn nêu trên. rootMargin cho phép bạn chỉ định lề cho gốc, giúp bạn tăng hoặc giảm vùng được dùng cho các giao điểm 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", chỉ định lề trên, lề phải, lề dưới và lề 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> phép thuật
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 chúng có đang hiển thị hay không. Nếu một <iframe> quan sát một trong các phần tử của nó, thì cả thao tác cuộn <iframe> cũng như thao tác cuộn cửa sổ chứa <iframe> đều sẽ kích hoạt lệnh gọi lại vào 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 rò rỉ dữ liệu trên các nguồn gốc.
IntersectionObserver không nói về điều gì?
Một điều cần lưu ý là IntersectionObserver không phải là pixel hoàn hảo cũng không phải là độ trễ thấp. Việc sử dụng các phương thức này để 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ẽ (nói một cách chính xác) đã lỗi thời vào thời điểm bạn sử dụng. Tài liệu 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 cho IntersectionObserver.
Tôi có thể thực hiện bao nhiêu công việc trong lệnh gọi lại?
Ngắn gọn và súc tích: 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.
Hãy tiếp tục và giao nhau các phần tử của bạn
IntersectionObserver được hỗ trợ tốt trên trình duyệt, vì có 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ó 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 polyfill mà một phương thứ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 bạn đã nghĩ ra điều gì.