Header co lại khi cuộn xuống là pattern phổ biến. Cần CSS transition cho mượt và JS detect vị trí cuộn.
Ví dụ mã nguồn
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style>
body {
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
margin: 0px;
padding: 0px;
height: 150vh;
}
.header {
position: fixed;
top: 0;
width: 100%;
background-color: rgb(52, 21, 110);
color: white;
padding: 50px;
font-size: 40px;
transition: all 0.3s ease; /* Mượt cho padding, font-size */
z-index: 1000;
}
.header.shrunk {
padding: 15px 50px;
font-size: 20px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
</style>
</head>
<body>
<div class="header" id="header">Header Text</div>
<p style="font-size: 35px; margin-top: 150px;">Lorem ipsum dolor sit amet...</p>
<script>
const header = document.getElementById('header');
window.addEventListener('scroll', () => {
if (window.scrollY > 50) {
header.classList.add('shrunk');
} else {
header.classList.remove('shrunk');
}
});
</script>
</body>
</html>
Cải tiến so với ví dụ gốc
- Dùng class .shrunk thay vì inline style - tách logic, dễ maintain
- transition: all 0.3s ease - Mượt cho cả padding, font-size, box-shadow
- window.scrollY - Cách hiện đại thay vì document.body.scrollTop
- Debounce/Throttle - Nên thêm cho hiệu năng (ví dụ dùng requestAnimationFrame)
Phiên bản tối ưu hiệu năng (Throttle)
let ticking = false;
window.addEventListener('scroll', () => {
if (!ticking) {
window.requestAnimationFrame(() => {
header.classList.toggle('shrunk', window.scrollY > 50);
ticking = false;
});
ticking = true;
}
});
Mở rộng
- Ẩn/hiện logo, navigation khi shrunk
- Thay đổi background opacity (blur backdrop-filter)
- Progress bar đọc bài viết ở đỉnh