Scroll Event เกิดขึ้นเมื่อผู้ใช้เลื่อน Scrollbar ของหน้าเว็บหรือองค์ประกอบที่มี Overflow เป็น Scroll/auto ใช้สำหรับ Infinite Scroll, Lazy Loading, Sticky Header, Parallax Effects ฯลฯ
ตัวอย่างพื้นฐาน: ตรวจจับ Scroll บน Window
<!DOCTYPE html>
<html lang="th">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Scroll Events</title>
<style>
body {
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
height: 150vh; /* สร้าง Scrollbar ให้ยาวพอ */
padding: 20px;
}
.result {
position: fixed;
top: 20px;
right: 20px;
background: rgba(0,0,0,0.8);
color: white;
padding: 15px;
border-radius: 8px;
font-family: monospace;
font-size: 14px;
z-index: 1000;
}
.content { margin-top: 100px; }
.section { height: 300px; border-bottom: 1px solid #eee; padding: 20px; }
</style>
</head>
<body>
<h1>Scroll Events ใน JavaScript</h1>
<div class="result" id="scrollInfo">เลื่อนหน้าจอเพื่อดูค่า Scroll</div>
<div class="content">
<div class="section"><h2>Section 1</h2><p>เลื่อนลงไป...</p></div>
<div class="section"><h2>Section 2</h2><p>ยังไม่ถึง...</p></div>
<div class="section"><h2>Section 3</h2><p>เกือบจบ...</p></div>
<div class="section"><h2>Section 4</h2><p>จบแล้ว!</p></div>
</div>
<script>
const scrollInfo = document.getElementById("scrollInfo");
// ใช้ Passive Listener เพื่อ Performance (ไม่เรียก preventDefault)
window.addEventListener("scroll", () => {
const scrollY = window.scrollY || window.pageYOffset;
const scrollX = window.scrollX || window.pageXOffset;
const docHeight = document.documentElement.scrollHeight - window.innerHeight;
const scrollPercent = Math.round((scrollY / docHeight) * 100);
scrollInfo.innerHTML = `
Scroll Y: ${scrollY}px
Scroll X: ${scrollX}px
Progress: ${scrollPercent}%
Viewport: ${window.innerWidth}x${window.innerHeight}
`;
}, { passive: true }); // สำคัญ: เพื่อ Performance
</script>
</body>
</html>
ผลลัพธ์
กล่องคงที่มุมขวาบน แสดงค่า Scroll Position, Progress % แทนที่จะแสดงแค่ "The scroll event has fired"
Performance Optimization (สำคัญมาก!)
1. ใช้ Passive Listeners
// ✅ ดี - Browser ไม่ต้องรอ preventDefault
window.addEventListener("scroll", handler, { passive: true });
// ❌ ไม่ดี - Browser ต้องรอดูว่าจะ preventDefault หรือไม่
window.addEventListener("scroll", handler);
2. Throttle / Debounce (จำกัดความถี่)
// Throttle: เรียกสูงสุด 1 ครั้งต่อ 100ms
function throttle(fn, limit) {
let inThrottle;
return function(...args) {
if (!inThrottle) {
fn.apply(this, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
window.addEventListener("scroll", throttle(() => {
// Heavy work here
updateStickyHeader();
checkLazyLoad();
}, 100), { passive: true });
3. ใช้ Intersection Observer แทน (Modern, ประหยัดกว่า)
// แทนที่ Scroll Event สำหรับ Lazy Loading / Infinite Scroll
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
loadImage(entry.target);
observer.unobserve(entry.target);
}
});
}, { rootMargin: "100px" }); // เริ่มโหลดก่อนเข้ามา 100px
document.querySelectorAll("img.lazy").forEach(img => observer.observe(img));
Scroll Event บน Element (ไม่ใช่ Window)
<div class="scroll-container" style="height: 300px; overflow-y: auto;">
<div class="content">...เนื้อหายาว...</div>
</div>
<script>
const container = document.querySelector(".scroll-container");
container.addEventListener("scroll", (e) => {
// e.target คือ container
const { scrollTop, scrollHeight, clientHeight } = e.target;
const isBottom = scrollTop + clientHeight >= scrollHeight - 10;
if (isBottom) {
loadMoreData();
}
}, { passive: true });
</script>
Scroll Event Properties
| Property | คำอธิบาย |
|---|---|
window.scrollY / pageYOffset | ตำแหน่ง Scroll แนวตั้ง (px) |
window.scrollX / pageXOffset | ตำแหน่ง Scroll แนวนอน (px) |
element.scrollTop | ตำแหน่ง Scroll ใน Element |
element.scrollLeft | ตำแหน่ง Scroll แนวนอนใน Element |
element.scrollHeight | ความสูงรวมของเนื้อหา (รวมส่วนที่ซ่อน) |
element.clientHeight | ความสูงส่วนที่มองเห็น (Viewport) |
Scroll Restoration (History Navigation)
// ควบคุม Scroll Position เมื่อ Back/Forward
history.scrollRestoration = "manual"; // "auto" (default) หรือ "manual"
window.addEventListener("popstate", () => {
// กู้ค่า Scroll Position ที่เก็บไว้
window.scrollTo(0, savedScrollPosition);
});