javascripteventsdomcontentloaded

Bind addEventListener DOMContentLoaded and Scroll issue


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.


Solution

  • 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>