Sticky headers without JavaScript scroll listeners
The old way used JavaScript scroll events and getBoundingClientRect to toggle a class. Sticky positioning does it in one property.
Code Comparison
✕ Old
15+ lines
1// JavaScript: scroll listener
2window.addEventListener('scroll', () => {
3 const rect = header.getBoundingClientRect();
4 if (rect.top <= 0) header.classList.add('fixed');
5 else header.classList.remove('fixed');
6});
7
8.header.fixed {
9 position: fixed;
10 top: 0;
11}
2window.addEventListener('scroll', () => {
3 const rect = header.getBoundingClientRect();
4 if (rect.top <= 0) header.classList.add('fixed');
5 else header.classList.remove('fixed');
6});
7
8.header.fixed {
9 position: fixed;
10 top: 0;
11}
✓ Modern
3 lines
1.header {
2 position: sticky;
3 top: 0;
4}
2 position: sticky;
3 top: 0;
4}
Why the modern way wins
No JavaScript
The browser handles scroll. No listeners, no getBoundingClientRect, no class toggles.
Respects flow
Sticky stays in layout until it hits the threshold, then sticks. No layout jumps.
One property
Set position and top. Works for headers, sidebars, or any element you want to pin.
Browser Support
97%
Lines Saved
15+ → 3
No JS needed
Old Approach
Scroll listener + class
JS + getBoundingClientRect
Modern Approach
One property
position: sticky
How it works
The old way meant a scroll listener, reading getBoundingClientRect on every scroll, and toggling a class to switch between normal and fixed. That's JS, reflows, and extra CSS for the fixed state.
With position: sticky and top: 0, the header stays in flow until it would scroll past the top, then it sticks. The browser does the work. No script, no class, no layout hacks.