2

I've got a list of tabs where if a user clicks on a tab for e.g. "Tab 1" then it'll show the copy that sits under this tab, and if they click on "Tab 2", the copy under this tab will show and the copy under "Tab 1" would close and so on.

This works how i want it to, but what I'm struggling with is if the user clicks on a tab i want them to be able to close the same tab. Any help on this would be appreciated.

document.querySelectorAll('.tabs-menu a').forEach(function(el) {
  el.addEventListener('click', function(event) {
    event.preventDefault();
    this.closest('.tabs-menu')
      .querySelector('.current')
      .classList.remove("current");
    this.closest('a').classList.add("current");
    document.querySelectorAll('.tab-content').forEach(function(a) {
      a.style.display = "none";
    });
    var activeTab = this.getAttribute("href");
    document.querySelector(activeTab).style.display = "block";
  });
});
.tab-content {
  display: none;
}
<ul class="tabs-menu">
  <li><a href="#tab-1">Tab 1</a></li>
  <div id="tab-1" class="current tab-content">tab 1 content</div>
  <li><a href="#tab-2">Tab 2</a></li>
  <div id="tab-2" class="tab-content">tab 2 content</div>
  <li><a href="#tab-3">Tab 3</a></li>
  <div id="tab-3" class="tab-content">tab 3 content</div>
  <li><a href="#tab-4">Tab 4</a></li>
  <div id="tab-4" class="tab-content">tab 4 content</div>
</ul>
isherwood
  • 58,414
  • 16
  • 114
  • 157
leek1234
  • 470
  • 2
  • 9
  • A div is not a valid child of a list. Partly for this reason, tab content panels are usually outside the list. – isherwood Jun 24 '21 at 14:20
  • @isherwood Thanks for your input, this is just a quick mockup of the original code which i unfortunately can't amend on the site so i have to work with what's there. – leek1234 Jun 24 '21 at 14:57

2 Answers2

1

I took another look at this and created the below which looks to work the way i want it to.

var acc = document.querySelectorAll('.tabs-menu li');
var i;
var open = null;
for (i = 0; i < acc.length; i++) {
  acc[i].addEventListener("click", function() {
    if (open == this) {
      open.classList.toggle("active");
      open = null;
    } else {
      if (open != null) {
        open.classList.toggle("active");
      }
      this.classList.toggle("active");
      open = this;
    }
  });
}
.tab-content {
  display: none;
}

.tabs-menu .active+.tab-content {
  display: block;
}
<ul class="tabs-menu">
  <li><a href="#tab-1">Tab 1</a></li>
  <div id="tab-1" class="tab-content">tab 1 content</div>

  <li><a href="#tab-2">Tab 2</a></li>
  <div id="tab-2" class="tab-content">tab 2 content</div>

  <li><a href="#tab-3">Tab 3</a></li>
  <div id="tab-3" class="tab-content">tab 3 content</div>

  <li><a href="#tab-4">Tab 4</a></li>
  <div id="tab-4" class="tab-content">tab 4 content</div>
</ul>
leek1234
  • 470
  • 2
  • 9
0

You're looking for the closest anchor element from an anchor element. I doubt that's what you want. Keep in mind that this always refers to the clicked element, not what you found in the line above.

Then, you just want to introduce a check for the current class and act accordingly:

document.querySelectorAll('.tabs-menu a').forEach(function(el) {
  el.addEventListener('click', function(event) {
    event.preventDefault();

    // if the tab is already active...
    if (this.classList.contains('current')) {

      // deactivate all tabs
      this.closest('.tabs-menu')
        .querySelector('.current')
        .classList.remove("current");

      // hide all tab panes
      document.querySelectorAll('.tab-content').forEach(function(pane) {
        pane.style.display = "none";
      });

      // otherwise...
    } else {

      // activate this tab
      this.classList.add("current");

      // hide all tab panes
      document.querySelectorAll('.tab-content').forEach(function(pane) {
        pane.style.display = "none";
      });

      // show the tab pane that correlates to this tab
      var activeTab = this.getAttribute("href");
      document.querySelector(activeTab).style.display = "block";
    }
  });
});
.tab-content {
  display: none;
}

a.current+.tab-content {
  display: block;
}
<ul class="tabs-menu">
  <li><a href="#tab-1">Tab 1</a></li>
  <div id="tab-1" class="tab-content">tab 1 content</div>

  <li><a href="#tab-2">Tab 2</a></li>
  <div id="tab-2" class="tab-content">tab 2 content</div>

  <li><a href="#tab-3">Tab 3</a></li>
  <div id="tab-3" class="tab-content">tab 3 content</div>

  <li><a href="#tab-4">Tab 4</a></li>
  <div id="tab-4" class="tab-content">tab 4 content</div>
</ul>

There's one final issue, and that's that the forEach loop is asynchronous, so sometimes the pane doesn't show because the call to show it runs ahead of the call to hide all panes. You'll need to implement a promise or other mechanism to deal with that. Read more

isherwood
  • 58,414
  • 16
  • 114
  • 157