Bài viết được sự cho phép của tác giả Lưu Bình An
Chúng ta thường phải đặt listener
trên sự kiện window.scroll
thực hiện một số thao tác tính toán, so sánh với thanh scroll để biết được khi nào element bắt đầu xuất hiện.
Cách làm này gây nhiều vấn đề hiệu năng và tương đối rườm rà. Giờ các trình duyệt đã đồng loạt hỗ trợ Intersection Observer API, chúng ta có một cách hoàn toàn gọn gàng, sạch sẽ mà lại tối ưu hiệu năng hơn nhiều.
Cách sử dụng như sau, chúng ta khởi tạo một instance IntersectionObserver
và gọi observe
trên element muốn theo dõi (watch là thuật ngữ chuyên ngành hơn)
const myImg = document.querySelector('.animate-me');
const observer = new IntersectionObserver((entry, observer) => {
console.log({ entry });
console.log({ observer });
})
observer.observe(myImg);
Trong trường hợp chúng ta muốn observe trên nhiều element cùng lúc
const myImgs = document.querySelectorAll('.animate-me');
const observer = new IntersectionObserver(entries => {
console.log(entries);
});
myImgs.forEach(image => {
observer.observe(image);
});
Để thực thi một tác vụ nào đó khi element bắt đầu xuất hiện trong viewport hoặc leave khỏi viewport
const myImgs = document.querySelectorAll('.animate-me');
observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
if (entry.intersectionRatio > 0) {
console.log('in the view');
} else {
console.log('out of view');
}
});
});
myImgs.forEach(image => {
observer.observe(image);
});
Với điều kiện intersectionRatio > 0
chúng ta biết được element đã xuất hiện trong viewport hay không
Với lazy load, chúng ta chỉ cần observe ở lần đầu tiên khi xuất hiện trên viewport, chúng ta sẽ unobserve
nó đi vì không cần tracking tiếp nữa
const myImgs = document.querySelectorAll('.animate-me');
observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
if (entry.intersectionRatio > 0) {
console.log('in the view');
observer.unobserve(entry.target);
} else {
console.log('out of view');
}
});
});
myImgs.forEach(image => {
observer.observe(image);
});
IntersectionObserver
API nhận thêm params thứ 2, để chúng ta truyền một số config
const config = {
rootMargin: '50px 20px 75px 30px',
threshold: [0, 0.25, 0.75, 1]
};
const observer = new IntersectionObserver(entry => {
// ...
}, config);
Các giá trị có thể truyền vào cho config
root
element dùng để kiểm tra intersection, nếunull
nó sẽ lấydocument
viewportrootMargin
: khai báo như giá trị margin css, ví dụ3rem 2rem
, có thể dùng để thêmoffset
cho intersection pointthrehold
: mảng giá trị từ 0 đến 1, tương ứng với ratio xuất hiện của element, 0 = hoàn toàn ra khỏi viewport, 1 là đang nằm trong viewport hoàn toàn,callback
sẽ được gọi vào tất cả các giá trị đã khai báo
Element được xem là nằm ngoài viewport khi nó đã nằm ngoài viewport + 15px margin
Ứng dụng 1: lazy load image
let observer = new IntersectionObserver(
(entries, observer) => {
entries.forEach(entry => {
/* Placeholder replacement */
entry.target.src = entry.target.dataset.src;
observer.unobserve(entry.target);
});
},
{rootMargin: "0px 0px -200px 0px"});
document.querySelectorAll('img').forEach(img => { observer.observe(img) });
Ứng dụng 2: Tự động pause video khi ra khỏi màn hình
let video = document.querySelector('video');
let isPaused = false; /* Flag for auto-paused video */
let observer = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if(entry.intersectionRatio!=1 && !video.paused){
video.pause(); isPaused = true;
}
else if(isPaused) {video.play(); isPaused=false}
});
}, {threshold: 1});
observer.observe(video);
Ứng dụng 3: Toggle class khi header sticky
const primaryNav = document.getElementById('primaryNav');
function callBack ([e]) {
e.target.classList.toggle("sticky", e.intersectionRatio < 1)
}
const observer = new IntersectionObserver(
callBack,
{ threshold: [1] }
);
observer.observe(primaryNav)
@media (prefers-reduced-motion: no-preference) {
.scroller {
scroll-behavior: smooth;
}
}
Tham khảo
- https://alligator.io/js/intersection-observer/
- https://css-tricks.com/a-few-functional-uses-for-intersection-observer-to-know-when-an-element-is-in-view/
- https://www.smashingmagazine.com/2021/07/dynamic-header-intersection-observer/
Bài viết gốc được đăng tải tại vuilaptrinh.com
Có thể bạn quan tâm:
- Angular – Tự xây dựng module “lazy load images”
- Kiến thức về “Lazy-loading images” mà bạn cần biết
- 6 ví dụ để bạn yêu luôn observable
Xem thêm Việc làm IT hấp dẫn trên TopDev