skip to Main Content

I followed this (Close javascript tabs when clicked again) to create tabs that open and close when clicked. Now, I also have a [x] mark to close the button and want the tab to close when I click EITHER one (or the tab again or the [x]):

<span onclick="this.parentElement.style.display='none'" class="topright">&times</span>

When I click on the tab again – it works fine (closes the tab and is ready to open the tab on the next click). But when I close the tab with the [x], I have to click once more on the tab to "re-activate" it before it’s ready to be open with the next click.
Would appreciate your help. Thanks.

function openTab(evt, tabName) {
  var activeClass = evt.target.classList.contains("active")
  
  if (!activeClass) {
    var i, tabcontent, tablinks;
    tabcontent = document.getElementsByClassName("tabcontent");
    for (i = 0; i < tabcontent.length; i++) {
      tabcontent[i].style.display = "none";
    }
    tablinks = document.getElementsByClassName("tablinks");
    for (i = 0; i < tablinks.length; i++) {
      tablinks[i].className = tablinks[i].className.replace(" active", "");
    }
    document.getElementById(tabName).style.display = "block";
    evt.currentTarget.className += " active";
  } else {
    document.getElementById(tabName).style.display = "none";
    evt.target.classList.remove("active");
  }
}

//document.getElementById("defaultOpen").click();
<div class="tab">
  <button class="tablinks" onclick="openTab(event, 'London')" id="defaultClosed">Tab name</button>
</div>

<div id="London" class="tabcontent">
  <span onclick="this.parentElement.style.display='none'" class="topright">&times</span>
  <h3>London</h3>
  <p>London is the capital city of England.</p>
</div>

3

Answers


  1. function openTab(evt, tabName) {
      var activeClass = evt.target.classList.contains("active");
    
      if (!activeClass) {
        var i, tabcontent, tablinks;
        tabcontent = document.getElementsByClassName("tabcontent");
        for (i = 0; i < tabcontent.length; i++) {
          tabcontent[i].style.display = "none";
        }
        tablinks = document.getElementsByClassName("tablinks");
        for (i = 0; i < tablinks.length; i++) {
          tablinks[i].classList.remove("active");
        }
        document.getElementById(tabName).style.display = "block";
        evt.currentTarget.classList.add("active");
      } else {
        document.getElementById(tabName).style.display = "none";
        evt.target.classList.remove("active");
      }
    }
    
    function closeTab(tabName) {
      document.getElementById(tabName).style.display = "none";
      var tablinks = document.getElementsByClassName("tablinks");
      for (var i = 0; i < tablinks.length; i++) {
        tablinks[i].classList.remove("active");
      }
    }
    .tabcontent {
      display: none;
    }
    
    .active {
      background-color: #ccc;
    }
    <div class="tab">
      <button class="tablinks" onclick="openTab(event, 'London')" id="defaultClosed">Tab name</button>
    </div>
    
    <div id="London" class="tabcontent">
      <span onclick="closeTab('London')" class="topright" style="cursor:pointer;">&times;</span>
      <h3>London</h3>
      <p>London is the capital city of England.</p>
    </div>
    Login or Signup to reply.
  2. It looks like the issue is related to the logic of toggling the active state when you close the tab using the [x] close button. When you close the tab with the [x], the openTab function still thinks it’s in the active state, so it requires another click to reset it.

    Instead of just setting display to none, you should also remove the active class from the corresponding tab button.

    Providing a function that removes the active class and sets display as none, should suffice:

    function closeTab(tabId, closeButton) {
      // Hide the tab content
      closeButton.parentElement.style.display = 'none';
      
      // Remove the active class from the tab button
      document.getElementById(tabId).classList.remove('active');
    }
    
    function openTab(evt, tabName) {
      var activeClass = evt.target.classList.contains("active");
      
      if (!activeClass) {
        var i, tabcontent, tablinks;
        tabcontent = document.getElementsByClassName("tabcontent");
        for (i = 0; i < tabcontent.length; i++) {
          tabcontent[i].style.display = "none";
        }
        tablinks = document.getElementsByClassName("tablinks");
        for (i = 0; i < tablinks.length; i++) {
          tablinks[i].className = tablinks[i].className.replace(" active", "");
        }
        document.getElementById(tabName).style.display = "block";
        evt.currentTarget.className += " active";
      } else {
        document.getElementById(tabName).style.display = "none";
        evt.target.classList.remove("active");
      }
    }
    <div class="tab">
      <button class="tablinks" onclick="openTab(event, 'London')" id="tab-London">Tab name</button>
    </div>
    
    <div id="London" class="tabcontent">
      <span onclick="closeTab('tab-London', this)" class="topright">&times;</span>
      <h3>London</h3>
      <p>London is the capital city of England.</p>
    </div>
    Login or Signup to reply.
  3. The reason why you face the issue has already been explained in a comment. I not going to repeat it but avoid it by coding clean to modern standards.

    First of all, if you use element.style.[...] you will look and set inline-styles. The modern solution is to avoid inline-styles but go for CSS-level solutions that use element.classList to avoid unattended specificity issues.

    The next thing is, that in modern JS you not coupling JS with the HTML by using the onclick-Attribute in HTML but by decoupling it by using an event listener in JS.

    I used some kind of event delegation where I just listened to the whole body and checked if the clicked element was either the close button or the close tab.

    Hide all tabcontent elements by default and only display them if they have the class active. That can be done depending if you need the support of older browsers or not with Level-4 Modules (Nesting):

    Level-4 Selector Module (Nesting):

    .tabcontent {
      display: none;
      &.active {
        display: block;
      }
    }
    

    Level-3 Selector Module (supports older browsers):

    .tabcontent {
      display: none;
    }
    
    .tabcontent.active {
      display: block;
    }
    

    If you click on the x to close an element you always want to close that element and you can use classList.remove('active').

    The logic is a little more complex if you click on the tabs to open the elements. There are 2 cases that can appear:

    1. Another element is open
    2. The element already is open
    3. There is no element open

    In both cases, you want to hide the currently opened element. In the 1st case, you additionally need to open the selected element.
    The logic here is to check if it is case 1 or either 2 or 3. If it is case 1 you need to add the active class to the selected element. Means you have a condition here. For adding classes based on a condition you can use classList.toggle('active', condition).
    The "special case" here is the 3rd case. If no element is open and you use querySelector('.active'), it returns null and the script will fail. Here you need to use a ? before classList so that this part will only execute if such an element exists.

    document.body.addEventListener('click', function(element) {
      let e = element.target;
      
      switch (true) {
        case e.classList.contains('tablinks'):
          let targetElement = document.getElementById(e.dataset.link);
          let isActive = targetElement.classList.contains('active');
          document.querySelector('.active')?.classList.remove('active');
          targetElement.classList.toggle('active', !isActive);
          break;
        case element.target.classList.contains('topright'):
          e.closest('.tabcontent').classList.remove('active');
          break;
      }
    })
    .tabcontent {
      display: none;
      &.active {
        display: block;
      }
    }
    <div class="tab">
      <button class="tablinks" data-link="London">London</button>
      <button class="tablinks" data-link="Washington">Washington D.C.</button>
      <button class="tablinks" data-link="Berlin">Berlin</button>
    </div>
    
    <div id="London" class="tabcontent active">
      <span class="topright">&times</span>
      <h3>London</h3>
      <p>London is the capital city of England.</p>
    </div>
    
    <div id="Washington" class="tabcontent">
      <span class="topright">&times</span>
      <h3>Washington D.C.</h3>
      <p>Washington D.C. is the capital city of the USA.</p>
    </div>
    
    <div id="Berlin" class="tabcontent">
      <span class="topright">&times</span>
      <h3>Berlin</h3>
      <p>Berlin is the capital city of Germany.</p>
    </div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search