leek1234
leek1234

Reputation: 470

How can I close the active tab with JavaScript?

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>

Upvotes: 2

Views: 236

Answers (2)

leek1234
leek1234

Reputation: 470

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>

Upvotes: 1

isherwood
isherwood

Reputation: 61055

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

Upvotes: 0

Related Questions