Reputation: 288
When we click in another tab in the main menu and we return to the first, the second menu (second tabs) loses its active state (no tab is open)
In addition the state is-active
on the links are deleted.
I don't know how not to affect the child tabs.
Here's a link to see the problem.
<div class="container js-tabs-container">
<div class="tabs">
<ul>
<li class="is-active" data-tab="tab-1"><a>Link 1</a></li>
<li data-tab="tab-2"><a>Link 2</a></li>
</ul>
</div>
<div class="js-tab-content" id="tab-1">
<p>Tab 1</p>
<!-- Nesting tabs -->
<div class="container js-tabs-container">
<div class="tabs">
<ul>
<li class="is-active" data-tab="tab-2-1"><a>Link 2-1</a></li>
<li data-tab="tab-2-2"><a>Link 2-2</a></li>
</ul>
</div>
<div class="js-tab-content" id="tab-2-1">
<p>Tab 2-1</p>
</div>
<div class="js-tab-content" id="tab-2-2">
<p>Tab 2-2</p>
</div>
</div>
</div>
<div class="js-tab-content" id="tab-2">
<p>Tab 2</p>
</div>
</div>
document.addEventListener('DOMContentLoaded', function () {
var tabs = document.getElementsByClassName('tabs');
if (tabs) {
var _loop = function _loop() {
var tabListItems = tabs[i].querySelectorAll('.tabs-menu-item');
tabListItems.forEach(function(tabListItem) {
// création d'un écouteur d'évènements sur le clic d'une tab
tabListItem.addEventListener('click', function () {
// suppression de la classe is-active sur chacune des tabs avant de la rajouter sur la tab qui a été cliquée
tabListItems.forEach(function(tabListItem) {
tabListItem.classList.remove('is-active');
});
tabListItem.classList.add('is-active');
// tabName correspond à la valeur de l'attribut data-tab
var tabName = tabListItem.dataset.tab;
// on identifie tous les contenus possibles puis on applique la classe has-display-none si l'ID du contenu ne correspond pas à la valeur de l'attribut data-tab
tabListItem.closest('.js-tabs').querySelectorAll('.js-tab-content').forEach(function(tabContent) {
if (tabContent.id !== tabName) {
tabContent.classList.add('has-display-none');
} else {
tabContent.classList.remove('has-display-none');
}
});
}, false);
});
};
for (var i = 0; i < tabs.length; i++) {
_loop();
}
}
});
Thanks
Upvotes: 2
Views: 311
Reputation: 7325
The problem you're having is caused by your use of querySelectorAll
which does not just select the first layer of child nodes, but will drill right into the DOM and retrieve ALL the matching nodes. A simple solution could be to differentiate the child tabs from the parent tabs by adding an attribute to differentiate them, and using a seperate handler for the children. A more comprehensive approach might involve only operating on the top level tabs and tab content.
(I've added English comments to the code and removed the foreign ones for the sake of other users readability)
'use strict';
document.addEventListener('DOMContentLoaded', function () {
var tabs = document.getElementsByClassName('tabs');
if (tabs) {
var _loop = function _loop() {
var tabListItems = tabs[i].querySelectorAll('li');
tabListItems.forEach(function (tabListItem) {
tabListItem.addEventListener('click', function (e) {
// Select any siblings of the current tab.
let siblings = Array.from(tabListItem.parentElement.children);
// Remove the is-active status from all siblings
siblings.forEach(function (element) {
element.classList.remove('is-active');
});
// Add the is-active status to the selected tab.
tabListItem.classList.add('is-active');
var tabName = tabListItem.dataset.tab;
// Same as above, rather than selecting all of the js-tab-content
// elements, we only select those which are at the same level as
// the selected tab.
let tabsContainer = tabListItem.closest('.js-tabs-container');
let tabsContainerChildren = Array.from(tabsContainer.children);
// Filter out other children that aren't js-tab-content elements.
let tabs = tabsContainerChildren.filter(function(el) {
return el.className.includes('js-tab-content')
});
tabs.forEach(function(tabContent) {
if (tabContent.id !== tabName) {
tabContent.classList.add('has-display-none');
} else {
tabContent.classList.remove('has-display-none');
}
})
}, false);
});
};
for (var i = 0; i < tabs.length; i++) {
_loop();
}
}
});
Assuming your structure stays generally the same, this should operate correctly for any level of nested tabs, as the only items affected when selected a tab are the screens and links around it.
Refactoring
Using arrow functions, and assigning less variables where they are unneeded will allow you to write shorter more succinct code. Here's an example, not necessarily the optimum way to write it, but it may give you some ideas for extracting functions, and chaining array iterations.
document.addEventListener('DOMContentLoaded', () => {
Array.from(document.getElementsByClassName('tabs')).forEach(tab => {
tab.querySelectorAll('li').forEach(listItem => {
listItem.addEventListener('click', tabClickHandler(listItem), false);
});
});
});
function tabClickHandler (listItem) {
return () => {
let siblings = Array.from(listItem.parentElement.children);
siblings.forEach(el => el.classList.remove('is-active'));
listItem.classList.add('is-active');
let tabName = listItem.dataset.tab;
Array.from(listItem.closest('.js-tabs-container').children)
.filter(el => el.classList.contains('js-tab-content'))
.forEach(tab => {
if (tab.id !== tabName) {
tab.classList.add('has-display-none');
} else {
tab.classList.remove('has-display-none');
}
});
}
}
Upvotes: 1