Search code examples
javascriptcssevent-listener

add className on scroll in JavaScript


I'm trying to add a className on scroll. I keep getting a

document is undefined

edit: I found out I was getting the error from the typo. When I define document.getElementsByClassName("main-nav").scrollTop nothing comes up in the console. As well as the page does not get affected.

window.onscroll = function() {
  windowScroll();
};

function windowScroll() {
  if (document.getElementsByClassName("main-nav").scrollTop > 50 || document.documentElement.scrollTop > 50) {
    document.getElementsByClassName("main-nav").className = "test";
  } else {
    document.getElementsByClassName("main-nav").className = "";
  }
}

CSS is

.test {
  background: pink
}

I'm not necessarily looking for the answer, I just want guidance


Solution

  • There are 2 problems:

    getElementsByClassName returns an array of HTMLCollection and it has no property scrollTop. You probably want the first item so the code shoul be document.getElementsByClassName("main-nav")[0] (or document.querySelector(".main-nav"))

    But if you try it, you will get an error:

    Cannot read property 'scrollTop' of undefined

    window.onscroll = function() {
      windowScroll();
    };
    
    function windowScroll() {
      if (document.getElementsByClassName("main-nav").scrollTop > 50 || document.documentElement.scrollTop > 50) {
        document.getElementsByClassName("main-nav").className = "test";
      } else {
        document.getElementsByClassName("main-nav").className = "";
      }
    }
    html, body {
      height: 150%;
    }
    
    .test {
      background: pink
    }
    <div class="main-nav"></div>

    The reason is that you override the class attribute of .main-nav by this assignment:

    document.getElementsByClassName("main-nav").className = "";
    

    In this line you set the class attribute to empty string. You probably want to add / remove the test call but keeping the main-nav class.

    There are 2 things you can do:

    1. Set the id attribute to main-nav instead of the class attribute, then use document.getElementById method.

    window.onscroll = function() {
      windowScroll();
    };
    
    function windowScroll() {
      if (document.getElementById("main-nav").scrollTop > 50 || document.documentElement.scrollTop > 50) {
        document.getElementById("main-nav").className = "test";
      } else {
        document.getElementById("main-nav").className = "";
      }
    }
    html, body {
      height: 150%;
    }
    
    #main-nav {
      position: fixed;
      width: 100%;
    }
    
    .test {
      background: pink
    }
    <div id="main-nav">Main Nav</div>

    1. Toggle only the test class using classList.toggle.

    window.onscroll = function() {
      windowScroll();
    };
    
    function windowScroll() {
      if (document.getElementsByClassName("main-nav")[0].scrollTop > 50 || document.documentElement.scrollTop > 50) {
        document.getElementsByClassName("main-nav")[0].classList.add("test");
      } else {
        document.getElementsByClassName("main-nav")[0].classList.remove("test");
      }
    }
    html, body {
      height: 150%;
    }
    
    .main-nav {
      position: fixed;
      width: 100%;
    }
    
    .test {
      background: pink
    }
    <div class="main-nav">Main Nav</div>


    The final approach with some optimisations:

    var mainNav = document.querySelector('.main-nav');
    
    window.onscroll = function() {
      windowScroll();
    };
    
    function windowScroll() {
        mainNav.classList.toggle("test", mainNav.scrollTop > 50 || document.documentElement.scrollTop > 50);
    }
    html, body {
      height: 150%;
    }
    
    .main-nav {
      position: fixed;
      width: 100%;
    }
    
    .test {
      background: pink
    }
    <div class="main-nav">Main Nav</div>

    The changes:

    1. Store the .main-nav element on the global context (the window object). It will not change so you don't need to find it in any scroll.
    2. Use querySelector so you will get a single DOM element, not collection.
    3. Use classList.toggle to toggle the class by condition.