-1

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;
  }

  • 1
    Yes, you need `querySelectorAll`, and you need to iterate over the resulting list of elements and assign the handler to each element separately: `document.querySelectorAll('.nav a').forEach(el => el.addEventListener(...));` –  Jun 30 '20 at 08:25
  • Thanks Chris G, awesome fix, that worked a treat. It seems I was on the right track I just didn't know how to implement it. Now I'm trying to figure out how to accept your answer... – Wayne R Hendricks Jun 30 '20 at 08:46
  • So thats to Chris G for his solution, here it is: ```// Select all nav elements function document.querySelectorAll('.nav a').forEach(el => el.addEventListener('click', function () { body.classList.remove('open'); })); ``` – Wayne R Hendricks Jun 30 '20 at 08:53
  • Duplicate: [How to addEventListener to multiple elements in a single line](https://stackoverflow.com/questions/40956717/how-to-addeventlistener-to-multiple-elements-in-a-single-line) –  Jun 30 '20 at 08:56
  • Thanks Chris, I see your answer in there. I've got it working. I was trying to accept your comment as an answer but I guess I can't do it. I have up-voted your comment though. Thanks alot. – Wayne R Hendricks Jun 30 '20 at 09:02
  • Not sure what you mean; your question is a duplicate, that's why I flagged it is as such; that will auto-post the comment with the link to the existing question. You cannot accept comments as answers, and I didn't post an answer because this question is a duplicate. –  Jun 30 '20 at 13:29
  • OK. I see. thank you. – Wayne R Hendricks Jun 30 '20 at 15:38
  • Sorry, I know this can seem off-putting, but it's just how stackoverflow works; rigorous closing and flagging is the only thing that keeps this website usable. –  Jun 30 '20 at 16:23

1 Answers1

1

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.

Cat
  • 4,141
  • 2
  • 10
  • 18
  • Thanks Cat, I've implemented your solution too and works perfectly. I'm definitely not a JS fundy. – Wayne R Hendricks Jun 30 '20 at 09:11
  • So to add it to the whole menu I would just change `a.nav-links` to `ul.nav-list`? – Wayne R Hendricks Jun 30 '20 at 09:12
  • You could use `selectElement('.nav-list')`. Then to know whether a link was clicked, you could change the listener to something like `function(event) { if(event.target.classList.contains('nav-link')){ /* do the stuff */}` – Cat Jun 30 '20 at 09:27