I'm building a page with scrolling sections and CSS
Snap. I also have a fixed header that switches its text color in black or white depending if the section in viewport has a dark or light background color.
Everything works fine but my Javascript
is not very optimized. Indeed, I need to create a load event to set the color of the fixed header when the page is loading. And also a scroll event when the user starts scrolling down the sections.
I've create to JS events and the problem is that I duplicate most part of the code. The objective is to bind in a better way the two events and not duplicate main part of the JS.
The tricky part is that the scroll
event doesn't happened in window but inside a div. So for the load event I have to use DOMContentLoaded
.
The JS for the DOMContentLoaded
event and the scroll
event:
const container = document.querySelector('.js-snap')
if (container) {
const header = document.querySelector(".js-header");
const sections = document.querySelectorAll('.js-scroll-post[data-theme="is-dark"]');
const isWhite = 'is-white';
// Load event
window.addEventListener("DOMContentLoaded", (e) => {
let headerIsWhite = false;
sections.forEach((section) => {
const fixed_position = window.scrollY;
const fixed_height = header.offsetHeight;
const toCross_position = section.offsetTop;
const toCross_height = section.offsetHeight;
// check if the header is within the bounds of the section
if (fixed_position < toCross_position + toCross_height && fixed_position + fixed_height > toCross_position) {
headerIsWhite = true;
}
});
if (headerIsWhite) {
header.classList.add(isWhite);
} else {
header.classList.remove(isWhite);
}
})
// Scroll event
document.querySelector(".js-snap-container").addEventListener("scroll", (e) => {
let headerIsWhite = false;
sections.forEach((section) => {
const fixed_position = e.target.scrollTop;
const fixed_height = header.offsetHeight;
const toCross_position = section.offsetTop;
const toCross_height = section.offsetHeight;
// check if the header is within the bounds of the section
if (fixed_position < toCross_position + toCross_height && fixed_position + fixed_height > toCross_position) {
headerIsWhite = true;
}
});
if (headerIsWhite) {
header.classList.add(isWhite);
} else {
header.classList.remove(isWhite);
}
})
}
The HTML:
<header class="site-header js-header"></header>
<div class="scroll js-snap">
<div class="scroll-container js-snap-container">
<div class="section is-dark"></div>
<div class="section"></div>
<div class="section is-dark"></div>
<div class="section is-dark"></div>
<div class="section"></div>
<div class="section"></div>
</div>
</div>
The CSS:
.site-header {
position: fixed;
width: 100%;
height: 3em;
top: 0;
left: 0;
color: black;
}
.site-header.is-white {
color: white;
}
.scroll-container{
width: 100%;
max-height: 100vh;
max-height: 100svh;
overflow: auto;
-webkit-overflow-scrolling: touch;
scroll-snap-type: y mandatory;
scroll-snap-points-y: repeat(100vh);
}
.section {
width: 100%;
height: 100vh;
background-color: white;
scroll-snap-align: start;
}
.section.is-dark {
background-color: black;
}
Thank you.
If I understand you correctly,you are looking for reusable / DRY (Don't Repeat Yourself) code
I additionally had to reset the CSS and call the scrollIt when loading the page
window.addEventListener("DOMContentLoaded", (e) => {
const container = document.querySelector('.js-snap');
if (container) {
const sections = document.querySelectorAll('.js-scroll-post[data-theme="is-dark"], .section.is-dark');
const header = document.querySelector(".js-header");
const isWhite = 'is-white';
const setClass = (headerIsWhite) => header.classList.toggle(isWhite, headerIsWhite);
const scrollIt = (e) => {
let headerIsWhite = false;
const fixed_position = e.target.scrollTop;
sections.forEach((section) => {
const fixed_height = header.offsetHeight;
const toCross_position = section.offsetTop;
const toCross_height = section.offsetHeight;
// check if the header is within the bounds of the section
if (fixed_position < toCross_position + toCross_height && fixed_position + fixed_height > toCross_position) {
headerIsWhite = true;
}
});
setClass(headerIsWhite);
};
// Initial check to set the header color based on the initial scroll position
scrollIt({
target: document.querySelector(".js-snap-container")
});
// Scroll event
document.querySelector(".js-snap-container").addEventListener("scroll", scrollIt);
}
});
html,
* {
margin: 0;
padding: 0
}
.site-header {
position: fixed;
width: 100%;
height: 3em;
top: 0;
left: 0;
color: black;
}
.site-header.is-white {
color: white;
}
.scroll-container {
width: 100%;
max-height: 100vh;
max-height: 100svh;
overflow: auto;
-webkit-overflow-scrolling: touch;
scroll-snap-type: y mandatory;
scroll-snap-points-y: repeat(100vh);
}
.section {
width: 100%;
height: 100vh;
background-color: white;
scroll-snap-align: start;
}
.section.is-dark {
background-color: black;
}
<header class="site-header js-header">HEADER</header>
<div class="scroll js-snap">
<div class="scroll-container js-snap-container">
<div class="section is-dark"></div>
<div class="section"></div>
<div class="section is-dark"></div>
<div class="section is-dark"></div>
<div class="section"></div>
<div class="section"></div>
</div>
</div>