0

I use this JS solution to make the navigation bar sticky when we scroll down the page. The problem is that I have multiple tabs on the page and each tab has the same navigation bar. Thus, this solution makes only the first navigation bar sticky. Is there a way to make this work for all of them?

In this code, I find objects by class name (it's intentional, I need to use search by class name, not by ID), but it finds the first object with such class name and therefore ignores the rest. How can I change this code so that it works with many objects with the same class names? I copy this navbar several times in different tabs of the page so class names are the same.

// When the user scrolls the page, execute myFunction
window.onscroll = function() {myFunction()};

// Get the navbar
var navbararray = document.getElementsByClassName("sticky-navbar");
var navbar = navbararray[0];

//Get the inner container
var innercontainerarray = document.getElementsByClassName("future-page-wrapper");
var innercontainer = innercontainerarray[0];
  
// Get the offset position of the navbar
var sticky = navbar.offsetTop;

// Add the sticky class to the navbar when you reach its scroll position. Remove "sticky" when you leave the scroll position
function myFunction() {
  if (window.pageYOffset >= sticky + 250) {
    navbar.classList.add("sticky");
    innercontainer.classList.add("page-wrapper");
  } else {
    navbar.classList.remove("sticky");
    innercontainer.classList.remove("page-wrapper")
  }
}
/* Style the navbar */
.sticky-navbar {
  overflow: hidden;
  background-color: #333;
}

/* Navbar links */
.sticky-navbar a {
  float: left;
  display: block;
  color: #f2f2f2;
  text-align: center;
  padding: 14px;
  text-decoration: none;
}

/* Page content */
.content {
  padding: 16px;
}

/* The sticky class is added to the navbar with JS when it reaches its scroll position */
.sticky {
  position: fixed;
  top: 0;
  width: 100%;
  left: 0;
}

/* Add some top padding to the page content to prevent sudden quick movement (as the navigation bar gets a new position at the top of the page (position:fixed and top:0) */
.sticky + .content {
  padding-top: 60px;
}

.page-wrapper {
  margin: 0px 250px;
}
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<div class="page-wrapper">
<div id="navbar" class="sticky-navbar">
  <div class="future-page-wrapper">
  <a href="#home">Home</a>
  <a href="#news">News</a>
  <a href="#contact">Contact</a>
  </div>
</div>
</div>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
Test
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
Rumata
  • 1,027
  • 3
  • 16
  • 47

1 Answers1

1

It looks like the primary issue is that you're explicitly grabbing the first of the navbars and applying the sticky logic. Instead, you'll want to iterate over all the navbars and apply your logic within that loop like so:

// Get the navbars
var navbararray = document.querySelectorAll(".sticky-navbar");

// rather than only operating on a single navbar, pass the whole array
window.onscroll = function () { myFunction(navbararray) };

// Add the sticky class to the navbar when you reach its scroll position. Remove "sticky" when you leave the scroll position
function myFunction(navbararray) {
  navbararray.forEach(navbar => {

  //Get the inner container
  var innercontainerarray = document.getElementsByClassName("future-page-wrapper");
  var innercontainer = innercontainerarray[0];

  // Get the offset position of the navbar
  var sticky = navbar.offsetTop;

  if (window.pageYOffset >= sticky + 250) {
    navbar.classList.add("sticky");
    innercontainer.classList.add("page-wrapper");
  } else {
    navbar.classList.remove("sticky");
    innercontainer.classList.remove("page-wrapper");
  }

  });
}

Edit: A little extra info - rather than using getElementsByClassName I used document.querySelectorAll('.sticky-navbar'). This gives you a non-live array of all objects with the aforementioned class (documentation here)

mclement
  • 26
  • 3
  • It worked and helped a lot, thank you very much! – Rumata Nov 28 '22 at 17:52
  • Interesting how this code would look like with document.querySelectorAll('.your-class') - would we still need the loop? – Rumata Nov 28 '22 at 17:52
  • @Rumata Yes, you'll still need the loop to iterate over the result of `querySelectorAll` - really the only reason to use it over `getElementsByClassName` as I've done is that it returns a non-live node list. – mclement Nov 28 '22 at 18:13
  • I don't work with JS very often, did I understand correctly that the advantage of using "querySelectorAll" is that it is better for performance? What would the whole code look like in this case? – Rumata Nov 28 '22 at 18:27
  • 1
    It's not necessarily better for performance (in fact it probably isn't) but it may be better for predictability in some cases (i.e. when modifying the list it returns - removing an element from a live list would change the overall list length mid loop for instance, whereas a non-live list would be unaffected). [This StackOverflow](https://stackoverflow.com/questions/14377590/queryselector-and-queryselectorall-vs-getelementsbyclassname-and-getelementbyid#answer-54819633) does a good job of explaining some of the particulars. `querySelectorAll` is in use in the example I posted above – mclement Nov 28 '22 at 19:22