skip to Main Content

First of all the "Outer tab 1" and "Outer tab 2" are under "Main tab 1" and "Outer tab 3" and "Outer tab 4" are under "Main tab 2".

Have patience with me, here is what I want. On page load/refresh the "Main tab 1" and "Outer tab 1" by default are active, and if I click the "Main tab 2" the "Outer tab 3" became the active tab.

Basically the only missing in my code are the active class on "Outer..." tabs when clicking its corresponding "Main..." tabs.

To better understand check the code below. Also here is the codepen https://codepen.io/.

const mainTabs = document.querySelectorAll(".main-tab-btn");
const childTabs = document.querySelectorAll(".child-tab-btn");
const contents = document.querySelectorAll(".content");
const buttons = document.querySelectorAll("button");

const childTabsBtn = document.querySelectorAll('[data-tab-value]');
const childTabsContent = document.querySelectorAll('[data-tab-info]')
    
function SetContent(tabIdx)
{
   // reset all contents
   contents.forEach(content => {
      content.classList.remove("active");
   });
   const content = document.getElementById(tabIdx);
   content.classList.add("active");
}

function ResetButtons(){
   buttons.forEach(btn=> {
     btn.classList.remove("active");
   });
}

// add eventlisteners to all the maintab buttons
mainTabs.forEach(function(tab){
  tab.addEventListener("click", function(e){
     var id = e.target.getAttribute("data-id");
     SetContent(id);
     ResetButtons();
     e.target.classList.add("active");
  });
});

// add eventlisteners to all the childtabs button
childTabs.forEach(function(tab){
  tab.addEventListener("click", function(e){
     var id = e.target.getAttribute("data-id");
     SetContent(id);
     ResetButtons();
     const button = document.querySelector(`.main-tab-btn[data-id="${id}"]`);
      button.classList.add("active");
      e.target.classList.add("active");
  })
})

//outer tab child button and outer content
childTabsBtn.forEach(tab => {
  tab.addEventListener('click', () => {
    const target = document
    .querySelector(tab.dataset.tabValue);
    childTabsContent.forEach(tabInfo => {
      tabInfo.classList.remove('active')
    })
    target.classList.add('active');
  })
})
@import url('https://fonts.googleapis.com/css2?family=Jost:wght@100;300;400;700&display=swap');

body {
  font-family: 'Jost', sans-serif;
  background-color: #f0f5ff;
}

h1 {
  font-size: 48px;
  color: #232c3d;
  text-align:center;
}

.wrapper {
  width: 590px;
  margin: auto;
  background-color: white;
  border-radius: 10px;
  box-shadow: 0px 5px 15px rgba(0, 0, 0, .1);
}

.buttonWrapper {
  display: grid;
  grid-template-columns: 1fr 1fr;
}

button {
  letter-spacing: 3px;
  border: none;
  padding: 10px;
  background-color: green;
  color: white;
  font-size: 18px;
  cursor: pointer;
  transition: 0.5s;
}

button:hover {
  background-color: none;
}

button.active {
  background-color: orange;
  pointer-events: none
}

.contentWrapper .content {
  display: none;
  padding: 1rem 2rem;
}

.contentWrapper .content.active {
  display: block;
  background-color: white;
}

[data-tab-info] {
  display: none;
}

.outer-content .active[data-tab-info] {
  display: block;
  padding: 1rem 2rem;
}
<h1>TOGGLE TABS</h1>
  
  <div class="wrapper">
    
    <div class="buttonWrapper">
      <button class="tab-button main-tab-btn active" data-id="maintab1">Main tab 1</button>
      <button class="tab-button main-tab-btn" data-id="maintab2">Main tab 2</button>
    </div>
    
    <div class="contentWrapper">
      <div class="content active" id="maintab1">
        Main Tab 1 Content
      </div>
      <div class="content" id="maintab2">
        Main Tab 2 Content
      </div>
    </div>
    
    <div class="outer-tab">
      <button class="child-tab-btn active" data-id="maintab1" data-tab-value="#child-tab-content-1">Outer tab 1</button>
      <button class="child-tab-btn" data-id="maintab1" data-tab-value="#child-tab-content-2">Outer tab 2</button>
      <button class="child-tab-btn" data-id="maintab2" data-tab-value="#child-tab-content-3">Outer tab 3</button>
      <button class="child-tab-btn" data-id="maintab2" data-tab-value="#child-tab-content-4">Outer tab 4</button>
    </div>
    
    <div class="outer-content">
      <div class="active" id="child-tab-content-1" data-tab-info>Outer tab 1 content</div>
      <div id="child-tab-content-2" data-tab-info>Outer tab 2 content</div>
      <div id="child-tab-content-3" data-tab-info>Outer tab 3 content</div>
      <div id="child-tab-content-4" data-tab-info>Outer tab 4 content</div>
    </div>
    
  </div>

2

Answers


  1. Replace this with your js. It will work

    const mainTabs = document.querySelectorAll(".main-tab-btn");
    const childTabs = document.querySelectorAll(".child-tab-btn");
    const contents = document.querySelectorAll(".content");
    const buttons = document.querySelectorAll("button");
    
    const childTabsBtn = document.querySelectorAll('[data-tab-value]');
    const childTabsContent = document.querySelectorAll('[data-tab-info]')
        
    function SetContent(tabIdx)
    {
       // reset all contents
       contents.forEach(content => {
          content.classList.remove("active");
       });
       const content = document.getElementById(tabIdx);
       content.classList.add("active");
     document.querySelector(`.child-tab-btn[data-id=${tabIdx}]`).classList.add('active')
    }
    
    function ResetButtons(){
       buttons.forEach(btn=> {
         btn.classList.remove("active");
       });
    }
    
    // add eventlisteners to all the maintab 
    mainTabs.forEach(function(tab){
      tab.addEventListener("click", function(e){
         var id = e.target.getAttribute("data-id");
         ResetButtons();
         SetContent(id);
         e.target.classList.add("active");
      });
    });
    
    // add eventlisteners to all the childtabs button
    childTabs.forEach(function(tab){
      tab.addEventListener("click", function(e){
         var id = e.target.getAttribute("data-id");
         SetContent(id);
         ResetButtons();
         const button = document.querySelector(`.main-tab-btn[data-id="${id}"]`);
          button.classList.add("active");
          e.target.classList.add("active");
      })
    })
    
    //outer tab child button and outer content
    childTabsBtn.forEach(tab => {
      tab.addEventListener('click', () => {
        const target = document
        .querySelector(tab.dataset.tabValue);
        childTabsContent.forEach(tabInfo => {
          tabInfo.classList.remove('active')
        })
        target.classList.add('active');
      })
    })

    If you want and explanation

    I haved added a code to add active class

    document.querySelector(`.child-tab-btn[data-id=${tabIdx}]`).classList.add('active')
    

    and
    sent this line back as it always reset the buttons so you have to add active after resetting the buttons

      ResetButtons();
      SetContent(id);
    
    Login or Signup to reply.
  2. You’re describing a tree structure in the text, but the markup isn’t a tree. Instead, you’re trying to emulate a tree using very dispersed HTML structure, which leads to complicated script and very error prone attributing of the HTML elements. Indexed ids and classes are not recommended, they’re a nightmare to maintain, especially when having dynamic content.

    That said, implement tabs using a real tree structure, you’ll get a light-weigth reusable script with minimum effort, something like this:

    const tabSys = document.querySelector('.tabbed');
    
    tabSys.addEventListener('click', e => {
      let index, branch, tab, actives,
        button = e.target.closest('button');
      if (!button) {return;} // Quit, not a button clicked
    
      // Get a branch and the correct tab to open
      index = button.getAttribute('data-index');
      branch = button.parentElement.querySelector('.tabs');
      tab = (branch.children)[index]; // index connects buttons to HTML structure
    
      // Hide active tabs in other branches
      actives = branch.querySelectorAll('.active');
      actives.forEach(active => {
        if (!active.contains(tab)) {
          active.classList.remove('active');
        }
      });
    
      // Show the selected tab
      tab.classList.add('active');
    
      // Open the first branch below the clicked button
      button = tab.querySelector('button');
      if (button) {
        button.click();
      }
    });
    .tab {
      display: none;
    }
    
    .active {
      display: block;
    }
    <div class="tabbed">
      <button data-index="0">Main tab 1</button>
      <button data-index="1">Main tab 2</button>
      <button data-index="2">Main tab 3</button>
      <div class="tabs">
        <div class="tab active">
          <div>Main Tab 1 Content</div>
          <button data-index="0">Subtab 1.1</button>
          <button data-index="1">Subtab 1.2</button>
          <div class="tabs">
            <div class="tab active">Subtab 1.1 content</div>
            <div class="tab">Subtab 1.2 content</div>
          </div>
        </div>
        <div class="tab">
          <div>Main Tab 2 Content</div>
          <button data-index="0">Subtab 2.1</button>
          <button data-index="1">Subtab 2.2</button>
          <div class="tabs">
            <div class="tab">Subtab 2.1 content</div>
            <div class="tab">Subtab 2.2 content</div>
          </div>
        </div>
        <div class="tab">
          <div>Main Tab 3 Content</div>
          <button data-index="0">Subtab 3.1</button>
          <button data-index="1">Subtab 3.2</button>
          <button data-index="2">Subtab 3.3</button>
          <div class="tabs">
            <div class="tab">Subtab 3.1 content</div>
            <div class="tab">Subtab 3.2 content</div>
            <div class="tab">
              <div>Subtab 3.3 content</div>
              <button data-index="0">Subtab 3.3.1</button>
              <button data-index="1">Subtab 3.3.2</button>
              <div class="tabs">
                <div class="tab">Subtab 3.3.1 content</div>
                <div class="tab">Subtab 3.3.2 content</div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>

    Notice, that all the styling classes are stripped off, that makes the use of the classes more clear in the answer, you can add all the needed classes to the elements, ofcourse.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search