Search code examples
javascripthtmlcssnav

Close nav menu overlay when clicking nav link. Only the first nav link works


I know this question has been asked before, but this one has a bit of a twist.

I've seen a few downvotes on this questions. Please be so kind as to let me know your reasons for downvoting so that I will be able to correct this in future. Thanks for your input all.

I have a single page scroll website with a nav menu overlay on screens smaller than 992px. The menu toggles fine, however when a nav link is clicked the nav menu remains open with the exception of the first nav-link.

I like to have every link closing the nav menu on click.

So How do get all the nav links to close the nav menu on click? I have a hunch it has to do with using querySelectorAll instead of just querySelector.

Here's a link to the site: https://portfolioprime.github.io/robotics/

Any assistance would be greatly appreciated.

Here's the navigation html.

HTML

<body>
  <header>
    
      <nav class="nav">
       
        <!-- Nav Menu -->

        <ul class="nav-list">

          <li class="nav-item">
            <a href="#showcase" class="nav-link">Home</a></li>
          <li class="nav-item">
            <a href="#robots" class="nav-link">Robots</a></li>
          <li class="nav-item">
            <a href="#projects" class="nav-link">Projects</a></li>
          <li class="nav-item">
            <a href="#research" class="nav-link">Research</a></li>
          <li class="nav-item">
            <a href="#explore" class="nav-link">Explore</a></li>
          <li class="nav-item">
            <a href="#prosthetics" class="nav-link">Prosthetics</a></li>
          <li class="nav-item">
            <a href="#contact" class="nav-link">Contact</a></li>
        </ul>

        <!-- Menu-toggle -->
        <div class="menu-toggle">
          <i class="fas fa-bars"></i>
          <i class="fas fa-times"></i>
        </div>

      </nav>
    
  </header>

</body>

And here's the Javascript.

JS

// Select element function
const selectElement = function (element) {
    return document.querySelector(element);
};

let menuToggler = selectElement('.menu-toggle');
let body = selectElement('body');
let menuClose = selectElement('.nav-link');

// Open/Close menu on .menu-toggle click
menuToggler.addEventListener('click', function () {
    body.classList.toggle('open');
});

// Close menu on .nav-link click
menuClose.addEventListener('click', function () {
    body.classList.remove('open');
});

And you may be interested in the CSS for the .open class that is appended to the body with javascript.

CSS

  .open .nav-list {
    bottom: 0;
  }
  .nav-link:hover {
    border-bottom: none;
  }
  .menu-toggle {
    display: block;
  }
  .open .menu-toggle .fa-bars {
    display: none;
  }
  .open .menu-toggle .fa-times {
    display: block;
    position: fixed;
    right: 2.7rem;
    top: 2rem;
  }


Solution

  • Your hunch is totally correct. This does it.

    // Select element function
    const selectElement = (element) =>
      document.querySelector(element);
    const getAllWithClass = (className) =>
      document.getElementsByClassName(className);
    
    const
      body = selectElement('body'),
      // Converts the returned collection to a proper Array
      navLinks = Array.from(getAllWithClass("nav-link"));
    
    // Close menu on .nav-link click
    navLinks.forEach(link => { // The Array method `forEach` loops through
      link.addEventListener('click', function() {
        body.classList.remove('open');
        console.log("(No more blue background means it's closed)");
      });
    });
    .open .nav-list {
      background-color: lightskyblue;
    }
    <body class="open">
      <ul class="nav-list">
        <li class="nav-item">
          <a href="#showcase" class="nav-link">Home</a></li>
        <li class="nav-item">
          <a href="#robots" class="nav-link">Robots</a></li>
        <li class="nav-item">
          <a href="#projects" class="nav-link">Projects</a></li>
      </ul>
    </body>

    Note: I think it would be better to add a single click-listener on the whole menu, (and check that the target of any click event is a nav-link before proceeding). But since you wanted to see how to add multiple listeners at once, I stuck with this.