Creating nested tabs using vanilla JavaScript, no need for jQuery

I am currently working on implementing tabs using a JavaScript function. The issue I am facing is that when I try to have tabs within one of the tabs, everything disappears every time I click on one of the inner tabs. This happens because the JavaScript function I am using sets the style.display to none.

My goal is to ensure that when doel1, doel2, or doel3 is clicked inside the 'POP' tab, the style.display of the 'POP' tab remains active.

Below is the JavaScript function I am using (sourced from w3school.com):

function openTab(evt, openTab) {
  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(openTab).style.display = "block";
  evt.currentTarget.className += " active";
}
.tab {
  overflow: hidden;
  border: 1px solid #ccc;
  background-color: #f1f1f1;
}

.tab button {
  background-color: inherit;
  float: left;
  border: none;
  outline: none;
  cursor: pointer;
  padding: 14px 16px;
  transition: 0.3s;
  font-size: 17px;
}

.tab button:hover {
  background-color: #ddd;
}

.tab button.active {
  background-color: #ccc;
}

.tabcontent {
  display: none;
  padding: 6px 12px;
  border: 1px solid #ccc;
  border-top: none;
}
<div class="tab">
  <button class="tablinks" onclick="openTab(event, 'bla')">bla</button>
  <button class="tablinks" onclick="openTab(event, 'blabla')">blabla</button>
  <button class="tablinks" onclick="openTab(event, 'pop')">POP</button>
</div>

<div id="bla" class="tabcontent">
  <h2>bla</h2>
  <table>
    <tr>
      <td>My stuff that works :)</td>
    </tr>
  </table>
</div>
<div id="blabla" class="tabcontent">
  <h3>blabla</h3>
  <table>
    <tr>
      <td>My stuff that works ;)</td>
    </tr>
  </table>
</div>

<div id="pop" class="tabcontent">
  <h1>POP</h1>
  <div class="tab">
    <button class="tablinks" onclick="openTab(event, 'doel1')">Doel 1</button>
    <button class="tablinks" onclick="openTab(event, 'doel2')">Doel 2</button>
    <button class="tablinks" onclick="openTab(event, 'doel3')">Doel 3</button>
  </div>
  <div id="doel1" class="tabcontent">
    My stuff that doesn't show when I want :(
  </div>
</div>

Answer №1

Consider this solution for your problem: I have updated the function to include an additional parameter indicating if it is a sub tab. When it is a sub tab, the parent tab will remain active while the child tab becomes active.

function openTab(evt, openTab, subTab) {
    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", "");
    }
    if(subTab) {
      var parent = evt.currentTarget.closest('.tabcontent');
      parent.style.display = "block";
      parent.className += " active";
    }
    document.getElementById(openTab).style.display = "block";
    evt.currentTarget.className += " active";

}
.tab {
overflow: hidden;
border: 1px solid #ccc;
background-color: #f1f1f1;
}


.tab button {
background-color: inherit;
float: left;
border: none;
outline: none;
cursor: pointer;
padding: 14px 16px;
transition: 0.3s;
font-size: 17px;
}


.tab button:hover {
background-color: #ddd;
}


.tab button.active {
background-color: #ccc;
}

.tabcontent {
display: none;
padding: 6px 12px;
border: 1px solid #ccc;
border-top: none;
}
<div class="tab">
    <button class="tablinks" onclick="openTab(event, 'bla')">bla</button>
    <button class="tablinks" onclick="openTab(event, 'blabla')">blabla</button>
    <button class="tablinks" onclick="openTab(event, 'pop')">POP</button>
</div>

<div id="bla" class="tabcontent">
    <h2>bla</h2>
    <table>
        <tr><td>My stuff that works :)</td></tr>
    </table>
</div>
<div id="blabla" class="tabcontent">
    <h3>blabla</h3>
    <table>
        <tr><td>My stuff that works ;)</td></tr>
    </table>
</div>

<div id="pop" class="tabcontent">
    <h1>POP</h1>
    <div class="tab">
        <button class="tablinks" onclick="openTab(event, 'doel1',true)">Doel 1</button>
        <button class="tablinks" onclick="openTab(event, 'doel2', true)">Doel 2</button>
        <button class="tablinks" onclick="openTab(event, 'doel3', true)">Doel 3</button>
    </div>
    <div id="doel1" class="tabcontent">
        My stuff that doesn't show when I  want :(
    </div>

    <div id="doel2" class="tabcontent">
        My stuff doel2
    </div>

    <div id="doel3" class="tabcontent">
        My stuff doel3
    </div>
</div>

Answer №2

I've made some adjustments here to target specific tabs for modification:

html:

<div class="tabs">
  <ul>
    <li>
      <button class="tablink active" onclick="openTab(event, 'tab1', 'parent')">
        Tab 1
      </button>
    </li>
    <li>
      <button class="tablink" onclick="openTab(event, 'tab2', 'parent')">
        Tab 2
      </button>
    </li>
    <li>
      <button class="tablink" onclick="openTab(event, 'tab3', 'parent')">
        Tab 3
      </button>
    </li>
  </ul>
</div>

<div id="tab1" class="parent active">
  <h2>Tab 1</h2>
</div>

<div id="tab2" class="parent">
  <h2>Tab 2</h2>
</div>

<div id="tab3" class="parent">
  <h2>Tab 3</h2>
  <div class="tabs">
    <ul>
      <li>
        <button class="subtablink active" onclick="openTab(event, 'tab1', 'child')">
          Tab 3a
        </button>
      </li>
      <li>
        <button class="subtablink" onclick="openTab(event, 'tab2', 'child')">
          Tab 3b
        </button>
      </li>
    </ul>
  </div>

  <div id="tab1" class="child active">
    <h2>Tab 3a</h2>
  </div>

  <div id="tab2" class="child">
    <h2>Tab 3b</h2>
  </div>

</div>

CSS:

.parent, .child {
  display: none;
}

.parent.active, .child.active {
  display: block;
}

.tablink.active, .subtablink.active {
  background: red;
}

Javascript:

function openTab(event, tab, set) {
  // Hide all tabs in scope
  const className = '.' + set;
  document.querySelectorAll(className).forEach(function(item) {
    item.className = set;
  });

  // Show selected tab
  const fullPath = className + '#' + tab;
  document.querySelector(fullPath).classList.add('active');

  // Set Tab to active (and deactivate others)
  const siblingClass = event.target.className;
  document.querySelectorAll('.' + siblingClass).forEach(function(item) {
    item.classList.remove('active');
  });
  event.target.classList.add('active');
}

I've implemented some basic styling adjustments. You can see it in action here: https://codepen.io/anon/pen/VyzWPQ

Just to clarify, the tab navigation links have unique classes (tablink & subtablink) based on their scope. You can customize these names as needed, especially if you have sub tabs within another parent tab. The javascript utilizes this to remove the active class from siblings when a button is clicked.

Answer №3

function switchTab(event, tabName, subTab) {
    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", "");
    }
    if(subTab) {
      var parent = event.currentTarget.closest('.tabcontent');
      parent.style.display = "block";
      parent.className += " active";
    }
    document.getElementById(tabName).style.display = "block";
    event.currentTarget.className += " active";

}
.tab {
overflow: hidden;
border: 1px solid #ccc;
background-color: #f1f1f1;
}


.tab button {
background-color: inherit;
float: left;
border: none;
outline: none;
cursor: pointer;
padding: 14px 16px;
transition: 0.3s;
font-size: 17px;
}


.tab button:hover {
background-color: #ddd;
}


.tab button.active {
background-color: #ccc;
}

.tabcontent {
display: none;
padding: 6px 12px;
border: 1px solid #ccc;
border-top: none;
}
<div class="tab">
    <button class="tablinks" onclick="switchTab(event, 'bla')">bla</button>
    <button class="tablinks" onclick="switchTab(event, 'blabla')">blabla</button>
    <button class="tablinks" onclick="switchTab(event, 'pop')">POP</button>
</div>

<div id="bla" class="tabcontent">
    <h2>bla</h2>
    <table>
        <tr><td>My content here :)</td></tr>
    </table>
</div>
<div id="blabla" class="tabcontent">
    <h3>blabla</h3>
    <table>
        <tr><td>My content here ;)</td></tr>
    </table>
</div>

<div id="pop" class="tabcontent">
    <h1>POP</h1>
    <div class="tab">
        <button class="tablinks" onclick="switchTab(event, 'doel1',true)">Goal 1</button>
        <button class="tablinks" onclick="switchTab(event, 'doel2', true)">Goal 2</button>
        <button class="tablinks" onclick="switchTab(event, 'doel3', true)">Goal 3</button>
    </div>
    <div id="doel1" class="tabcontent">
        My content that doesn't show when I want :(
    </div>

    <div id="doel2" class="tabcontent">
        My content goal 2
    </div>

    <div id="doel3" class="tabcontent">
        My content goal 3
    </div>
</div>
Thank you :)

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Equivalent of ResolveUrl for loading scripts without the need for server technology

I am in the process of converting an ASP.NET web page into a plain HTML web page. Most of the work is done, however, I am having trouble replacing the ASP.NET Page.ResolveUrl function when setting a reference to a javascript file: <script src="<%= ...

Transform the appearance of buttons within AppBar using Material UI React

Embarking on a new project using React and Node JS has led me into the battle with Material UI. My current challenge is customizing the style of AppBar items, particularly the Buttons. Here's what I have in my "Menu" component: const Menu = () => ...

The HTML date input is displaying the date incorrectly, appearing one month off when set via the value attribute

i have tried this in two different browsers 1.Chromium Version 76.0.3809.87 2.Mozilla firefox 68.0.1 every time i enter a date in the input field in the format y-m-d, the month seems to be reduced by one without any explanation this is my code <i ...

Optimal-minimal behavior with a versatile CSS grid system

Currently, the use of grid-template-columns: repeat(auto-fit, minmax(25rem, 1fr)); Results in this behavior: If there are more items than can fit in a row (based on the minimum), new rows are created. If there are fewer items than the row could accommodat ...

Library for creating custom password input fields with a text type

We are currently facing an issue on our website where autofill is not working correctly due to having two password input fields - one for login and one for the password. Browsers are unable to remember the login information and paste the password into th ...

How can I transition smoothly between two colors in three.js?

I am working with a three.js object that is currently a specific color and I would like to smoothly animate it to a different color. I want the animation to only display a direct transition between the initial and final colors without following a linear pa ...

Intellij IDEA does not offer auto-completion for TypeScript .d.ts definitions when a function with a callback parameter is used

I've been working on setting up .d.ts definitions for a JavaScript project in order to enable auto-completion in Intellij IDEA. Here is an example of the JavaScript code I'm currently defining: var testObj = { tests: function (it) { ...

The error message "Uncaught ReferenceError: require is not defined" is commonly encountered when using Webpack

Despite countless similar questions, none have provided a solution to my issue because the underlying problem seems to elude me. I am attempting to bundle files for the browser using webpack 4.26.1, but no matter what I try, I consistently encounter the er ...

The error message "Cannot construct apickli.Apickli" is indicating a Type Error

Encountering this issue : TypeError: apickli.Apickli is not a constructor every time I attempt to execute Sendpostrequest.js again in the following step of a scenario. In A.js, the initial call to Sendpostrequest works without any problems. However, ...

How can I address multiple buttons with various events using jQuery?

I am new to learning jQuery and I'm currently working on some exercises. However, I've run into an issue with two buttons in the DOM that are supposed to perform different actions. I can't seem to figure out how to assign different functions ...

Proper alignment of div elements using Bootstrap

I am attempting to align 2 div elements side by side using Bootstrap code: <div class='col-12'> <div class='row'> <div class='col-6'> </div> <div class='col-6&a ...

Can state values be utilized as content for Meta tags?

I am looking for a way to display image previews and titles when sharing a page link. In order to achieve this, I am using the Nextjs Head Component. The necessary details are fetched on page load and used as content for the meta attributes. let campaign = ...

Exploring the implementation of waterfall in a Node.js application

async.traverse(map, function(item, tnext){ async.waterfall([ function(wnext){ console.log("One"); //performing MongoDB queries db.collection.find().toArray(function(err){ if(err){ ...

Improving the structure of Vue single-file components

As my single-file component continues to grow, I decided to do some refactoring. It turns out that a couple of dialogs are making the .vue file quite large (800+ lines). To maintain a clean and organized component, I am working on transforming these dialog ...

Creating a Dynamic Slideshow on Autopilot

My JavaScript skills are not perfect and I'm feeling a bit lost. I have this code for a slideshow, but I want to make it automatic while also allowing users to navigate between images freely. I'm struggling to figure out how to implement this fun ...

"Learn how to transfer a selected value from a parent ASP.NET dropdownlist to a textbox in a popup window using JavaScript

I'm struggling to pass the value from a dropdown list in the parent ASPX form to a textbox in the child ASPX form. Parent JavaScript: The first script is used to open the popup window <script type="text/javascript"> var popup; ...

React Semantic UI Grid and Div components can be toggled by using a button to decrease

My goal is to create a menu that initially displays with a certain width, but when a button is clicked, it will decrease the width to 50px and only show the icon. I'm struggling to figure out how to adjust the width of my div and integrate this funct ...

"Exploring the Depths: How Node.js Utilizes Recursive Calls

I need assistance generating a comprehensive list of all users' flairs on a specific subreddit. To achieve this, Reddit breaks down the requests into chunks of 1,000 and offers both "before" and "after" parameters for fetching purposes. However, I am ...

An issue with the THREE.js TextureLoader not functioning properly when deployed

After implementing the code below, my project is successfully rendering my background and avatar. However, when deploying on Netlify, the rendering does not function as expected. I have attempted using "../../", "./", and other variations, but the issue p ...

Dealing with undefined or null values when using ReactJS with Formik

Issue Resolved: It seems that Formik requires InitialValues to be passed even if they are not necessary. I'm currently working on a formik form in React, but every time I click the submit button, I encounter an error message stating "TypeError: Canno ...