Một trang có nhiều thẻ nội dung không nhất thiết phải khởi tạo mọi phần bổ sung ngay khi tải. Có thể đợi phần tử đến gần vùng nhìn rồi mới kích hoạt phần nâng cấp. IntersectionObserver giúp nhận thông báo giao cắt thay vì tự đo vị trí từng phần tử trong mọi sự kiện scroll.
Bài viết trình bày một mẫu kích hoạt một lần. Ví dụ chỉ đánh dấu trạng thái cho thẻ, không gọi API hay tải tài nguyên thật; khi tích hợp vào sản phẩm cần kiểm tra trên browser và layout thực tế.
1. Hiểu đúng tín hiệu đang quan sát
Theo MDN Intersection Observer API, observer báo thay đổi giao cắt giữa target với root hoặc viewport theo cấu hình. Callback được gọi bất đồng bộ và có thể có entry ban đầu ngay sau khi bắt đầu quan sát.
Điều đó không đồng nghĩa người dùng đã đọc nội dung. Một phần tử gần viewport, bị che bởi lớp khác hoặc xuất hiện rất nhanh khi cuộn chưa đủ làm bằng chứng về mức độ chú ý. Đừng biến một callback giao cắt thành số liệu “đã xem quảng cáo” nếu chưa có định nghĩa và kiểm chứng riêng.
2. Chọn root, rootMargin và threshold theo mục tiêu
| Tùy chọn | Cách hiểu | Ví dụ quyết định |
|---|---|---|
| root | Vùng gốc dùng để tính giao cắt; null dùng viewport | Trang thường khác danh sách cuộn trong panel |
| rootMargin | Mở rộng hoặc thu hẹp vùng tính toán | Thêm khoảng đệm phía dưới để chuẩn bị sớm |
| threshold | Ngưỡng tỷ lệ giao cắt cần nhận thông báo | 0 phù hợp tín hiệu bắt đầu giao cắt; không phải lịch gọi mỗi pixel |
Với rootMargin = '0px 0px 200px 0px', phía dưới vùng gốc được nới thêm 200 px. Phần tử có thể kích hoạt khi chưa nằm trong vùng nhìn thật. Con số 200 là ví dụ cần điều chỉnh theo tốc độ tải và trải nghiệm, không phải mặc định tối ưu cho mọi thiết bị.
Threshold 1 có thể không đạt với target cao hơn vùng chứa. Vì vậy, yêu cầu “đợi nhìn thấy toàn bộ” cần được xét cùng kích thước phần tử. Đặc tả Intersection Observer của W3C mô tả mô hình giao cắt và xử lý thông báo.
3. Một mẫu kích hoạt một lần có cleanup
function watchNearViewport(targets, activate) {
const pending = new Set(targets);
if (!('IntersectionObserver' in window)) {
pending.forEach(activate);
return () => {};
}
let stopped = false;
const observer = new IntersectionObserver((entries) => {
if (stopped) return;
for (const entry of entries) {
if (!entry.isIntersecting || !pending.delete(entry.target)) continue;
observer.unobserve(entry.target);
activate(entry.target);
}
}, {
root: null,
rootMargin: '0px 0px 200px 0px',
threshold: 0,
});
pending.forEach((target) => observer.observe(target));
return () => {
stopped = true;
pending.clear();
observer.disconnect();
};
}
Hàm nhận tập target cố định và callback kích hoạt đồng bộ, không ném lỗi. Set pending ngăn cùng target được kích hoạt hai lần bởi các entry lặp. isIntersecting cho biết trạng thái giao cắt của entry; không nên kích hoạt chỉ vì callback đã chạy.
Mẫu bỏ quan sát target trước khi kích hoạt. Nếu thay callback bằng thao tác mạng có thể thất bại, cần thiết kế thêm trạng thái loading/error, retry và giới hạn concurrency. Không coi mẫu một lần này là một bộ tải dữ liệu có retry hoàn chỉnh.
4. Gắn vào DOM sau khi phần tử tồn tại
const stopWatching = watchNearViewport(
document.querySelectorAll('[data-card]'),
(card) => { card.dataset.nearViewport = 'true'; },
);
// Gọi stopWatching() trong bước hủy component hoặc rời route.
Chạy đoạn này trong script type=module, script defer hoặc sau khi DOM cần quan sát đã được tạo. querySelectorAll trả tập phần tử tại thời điểm gọi; thẻ được thêm sau đó cần được đăng ký trong một vòng đời phù hợp.
disconnect() ngừng quan sát các target của observer. Nó không tự hủy request mạng đã bắt đầu, timer hay listener khác. Nếu component tạo những tài nguyên đó, phải dọn riêng.
5. Giữ trải nghiệm cơ bản độc lập với observer
Nội dung chính và liên kết nên đọc được khi JavaScript bị lỗi hoặc API không có. Ví dụ fallback gọi activate ngay; nó không thay thế thiết kế HTML ban đầu có thể sử dụng được.
Đừng giấu cả bài hoặc nút quan trọng rồi chờ observer để hiện. Nếu phần nâng cấp có animation, tôn trọng lựa chọn giảm chuyển động; nếu thêm nội dung làm đổi bố cục, dành sẵn không gian phù hợp. Với ảnh thông thường, kiểm tra trước xem loading='lazy' đã đủ nhu cầu chưa.
6. Checklist kiểm tra trên browser
- Target đang trong viewport lúc tải trang vẫn được xử lý.
- Target ngoài vùng gốc chưa bị đánh dấu cho tới khi đạt điều kiện.
- Cuộn qua lại không kích hoạt lặp mẫu một lần.
- Panel cuộn riêng dùng root đúng; thử cả viewport mobile.
- Rời route hoặc hủy component gọi cleanup.
- Thẻ thêm động có kế hoạch đăng ký riêng.
- Không có API hoặc JavaScript lỗi vẫn giữ nội dung chính sử dụng được.
- Đo công việc trong callback: dùng observer không làm tác vụ nặng tự biến mất.
Ví dụ trong bài được kiểm tra cú pháp JavaScript; chưa có kết quả kiểm thử browser hoặc benchmark để khẳng định cải thiện tốc độ. Hãy đo trước/sau trên trang thật, gồm thiết bị yếu và đường mạng chậm.
IntersectionObserver phù hợp khi ứng dụng cần một tín hiệu giao cắt có ngưỡng. Chọn vùng gốc đúng, giữ callback nhỏ và quản lý vòng đời rõ ràng sẽ hữu ích hơn việc thêm observer vào mọi thành phần.




Chưa có bình luận. Hãy là người đầu tiên chia sẻ ý kiến.