Search code examples
htmlcssbootstrap-5

Add a close button to tabs


I am learning bootstrap, and I want to have an x next to each tab name to close it:

<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">

<ul class="nav nav-tabs" id="myTab" role="tablist">
  <li class="nav-item" id="tab-li-1">
    <button
      class="nav-link active"
      data-bs-toggle="tab"
      data-bs-target="#tab-1"
      type="button"
    >
      First
      <button
        class="btn-close"
        onclick="
          document.getElementById(`tab-li-1`).remove();
          document.getElementById(`tab-1`).remove();
        "
       ></button>
    </button>
  </li>
  <li class="nav-item" id="tab-li-2">
    <button
      class="nav-link"
      data-bs-toggle="tab"
      data-bs-target="#tab-2"
      type="button"
     >
     Second
       <button
        class="btn-close"
        onclick="
          document.getElementById(`tab-li-2`).remove();
          document.getElementById(`tab-2`).remove();
        "
       ></button>
     </button>
  </li>
</ul>
<div class="tab-content" id="myTabContent">
  <div class="tab-pane fade show active" id="tab-1" tabindex="0">Some text here for `First`</div>
  <div class="tab-pane fade" id="tab-2" tabindex="0">More text here for `Second`</div>
</div>

    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-kenU1KFdBIe4zVF0s0G1M5b4hcpxyD9F7jL+jjXkk+Q2h455rYXK/7HAuoJl+0I4" crossorigin="anonymous"></script>

I was able to hack something around nesting another button inside my first button and then:

document.getElementById(tabId).remove();
document.getElementById(tabContentId).remove();

but I have found online that it is not valid to nest a button inside another button

Is there a better alternative that people can suggest?


Solution

  • You don't need to nest buttons. You don't have to use a buttons for your tabs and can just use a normal div. For accessibility reasons, you should use role="button" therefor.

    Instead of hardcoding the buttons you could simply use JS to create the close buttons on every tab.

    Your script should remove not only the tab itself but also the connected tab-pane:

    window.addEventListener('DOMContentLoaded', function() {
      const TABS = document.querySelectorAll('.nav-link');
    
      //adds clsoe button to all tabs
      TABS.forEach(tab => {
        let closeBtn = document.createElement('button');
        closeBtn.ariaLabel = 'Close Button';
        closeBtn.textContent = 'X';
        closeBtn.addEventListener('click', removeTab); 
        tab.appendChild(closeBtn);
      })
      
      //removes the tab and the tab-pane
      function removeTab() {
        let tabId = this.closest('.nav-link').dataset.bsTarget;
        let tabPaneElement = document.querySelector(tabId);
        let liElement = this.closest('li');
        tabPaneElement.remove();
        liElement.remove();
      }
    })
    .nav-link {
      &:hover {
        cursor: pointer;
      }
      button {
        margin-left: 1em;
      }
    }
    <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">
    
    <ul class="nav nav-tabs" id="myTab" role="tablist">
      <li class="nav-item" id="tab-li-1">
        <div class="nav-link active" data-bs-toggle="tab" data-bs-target="#tab-1" role="button">First</div>
      </li>
      <li class="nav-item">
        <div class="nav-link" data-bs-toggle="tab" data-bs-target="#tab-2" role="button">Second</div>
      </li>
    </ul>
    <div class="tab-content" id="myTabContent">
      <div class="tab-pane fade show active" id="tab-1" tabindex="0">Some text here for `First`</div>
      <div class="tab-pane fade" id="tab-2" tabindex="0">More text here for `Second`</div>
    </div>
    
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-kenU1KFdBIe4zVF0s0G1M5b4hcpxyD9F7jL+jjXkk+Q2h455rYXK/7HAuoJl+0I4" crossorigin="anonymous"></script>