1

I'm working on a basic dropdown element in HTML and jQuery and I'm trying to get better at understanding JavaScript and jQuery so this questions is a bit about code refactoring as well.

So here is what I've gotten so far:

HTML

<li class="nav-item">
    <a class="nav-link" href="#">Foo</a>
    <div class="subnav">
       ...
    </div>
</li>

JavaScript

const navLink = $('.nav-link');

navLink.each(function () {
    let $this = $(this);
    $this.click(function (e) {
        let hasSubnav = $this.parent().find('.subnav');
        if(hasSubnav.length !== 0) {
            e.preventDefault();
            $this.toggleClass('dropdown-active');
        }
        hasSubnav.stop(true, true).slideToggle(200);
    })
});

This solutions works fine. So what I want to do next is to check if another element in my loop is active, close is accordingly and then open the one I just clicked.

I thought about just putting a default click function before the each function like this:

navLink.click(function () {
    $('.subnav').slideUp();
});
navLink.each(function () {
    let $this = $(this);
    $this.click(function (e) {
        let hasSubnav = $this.parent().find('.subnav');
        if(hasSubnav.length !== 0) {
            e.preventDefault();
            $this.toggleClass('dropdown-active');
        }
        hasSubnav.stop(true, true).slideDown(200);
    })
});

But this does not seem to work. So my question is, is there a pretty way to achieve this maybe even inside of the each function? I've red about .not(this) in this post, which will maybe work (haven't tried it yet) but I thought that this would be duplicated code and that there might be a better way to get this to work.

2
  • 1
    There is no reason to use each here, since you're setting an event handler. Just navLink.on('click', function (e) { ... }) will apply the click function to all elements in navLink. Then, just set navLink.removeClass('dropdown-active') to remove the class from all, and $(this).addClass('dropdown-active') to add it to the current one. Commented Apr 3, 2019 at 12:36
  • Possible duplicate of toggleClass and remove class from all other elements Commented Apr 3, 2019 at 12:40

1 Answer 1

2

Your code is now looping through every single nav-link and adding a click handler to them one by one. It is possible to remove the each loop, since you can just add a click handler to all nav-links at once.

All you have to do is add a click handler to the nav-link and then remove the active class and slide up all open dropdowns before executing your logic. See working code example below for reference:

// Collapse all initially
$(".subnav").slideUp();

// Add click handler to all nav-links
const navLink = $('.nav-link');
navLink.click(function(e) {
  // Remove active classes on other elements & slide up
  const otherLinks = navLink.not(this);
  otherLinks.removeClass('dropdown-active');
  otherLinks.parent().find('.subnav').slideUp();
  
  // Slide down the subnav of selected element
  let hasSubnav = $(this).parent().find('.subnav');
  if (hasSubnav.length !== 0) {
    e.preventDefault();
    $(this).addClass('dropdown-active');
  }
  hasSubnav.stop(true, true).slideToggle(200);
})
<script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>

<li class="nav-item">
  <a class="nav-link" href="#">Foo</a>
  <div class="subnav">
    <a href="#">Link1</a>
    <a href="#">Link2</a>
    <a href="#">Link3</a>
  </div>
</li>
<li class="nav-item">
  <a class="nav-link" href="#">Foo</a>
  <div class="subnav">
    <a href="#">Link1</a>
    <a href="#">Link2</a>
    <a href="#">Link3</a>
  </div>
</li>

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.