I am trying to create tabs with vanilla JavaScript, each tab has an icon and a title inside the link
<div >
<a href="#tab3">
<img src="img/school.png">
<h3 >Instruktāžas datu aizsardzībā</h3>
</a>
</div>
It should open the tab content
<div id="tab3" >
The problem is that .getAttribute("href");
returns null when I have <img>
and <h3>
elements inside the link.
Everything works if I change the tab to
<div >
<img src="img/school.png">
<a href="#tab3">Instruktāžas datu aizsardzībā</a>
</div>
but I want the tab content to open when clicked anywhere on .tabs-bar-item
. How can it be done in vanilla JavaScript?
Full JS code:
let tabs = document.querySelectorAll('.tabs-bar .tabs-bar-item');
function tabClicks(tabClickEvent) {
for (let i = 0; i < tabs.length; i ) {
tabs[i].classList.remove("active");
}
let clickedTab = tabClickEvent.currentTarget;
clickedTab.classList.add("active");
tabClickEvent.preventDefault();
let contentPanes = document.querySelectorAll('.tabs-content-item');
for (i = 0; i < contentPanes.length; i ) {
contentPanes[i].classList.remove("active");
}
let anchorReference = tabClickEvent.target;
let activePaneId = anchorReference.getAttribute("href");
let activePane = document.querySelector(activePaneId);
activePane.classList.add("active");
}
for (i = 0; i < tabs.length; i ) {
tabs[i].addEventListener("click", tabClicks)
}
Css for tab content:
.tabs-content .tabs-content-item {
display: none;
}
.tabs-content .tabs-content-item.active {
display: block;
}
CodePudding user response:
First off : you can do this in pure CSS with the :target pseudo-class, no JS needed.
.tabs-content .tabs-content-item {
display: none;
}
.tabs-content .tabs-content-item:target {
display: block;
}
In the near future, you can also style the tab link for the currently open tab by using the :local-link pseudo-class.
In your code, tabClickEvent.currentTarget
always refers to the DIV element that you attached the listener to. That element has no href
. There are multiple ways to fix that. Removing the DIV altogether would be a good start, then you can attach a listener to the links, or make a global listener that checks if it's been clicked on a link or its descendant (using element.closest('a')
).
Either way, properly implementing navigation with JavaScript is not as easy as you might think (this is also why the pure CSS solution is good). For example, if the user wants to open the link in a new (browser) tab, you'd have to add some code to read the page's fragment on load and open the correct tab without user interaction.
CodePudding user response:
Element.closest searchs up the DOM to find an element matching the selector passed as argument. If Element
already matches the selector, Element
(the node on which closest
is called) is returned.
Hence try changing
let anchorReference = tabClickEvent.target;
to
let anchorReference = tabClickEvent.target.closest('a');
as for example in
let tabs = document.querySelectorAll('.tabs-bar .tabs-bar-item');
function tabClicks(tabClickEvent) {
for (let i = 0; i < tabs.length; i ) {
tabs[i].classList.remove("active");
}
let clickedTab = tabClickEvent.currentTarget;
clickedTab.classList.add("active");
tabClickEvent.preventDefault();
let contentPanes = document.querySelectorAll('.tabs-content-item');
for (i = 0; i < contentPanes.length; i ) {
contentPanes[i].classList.remove("active");
}
let anchorReference = tabClickEvent.target.closest('a');
console.log( anchorReference);
let activePaneId = anchorReference.getAttribute("href");
let activePane = document.querySelector(activePaneId);
activePane.classList.add("active");
}
for (i = 0; i < tabs.length; i ) {
tabs[i].addEventListener("click", tabClicks)
}
.tabs-content .tabs-content-item {
display: none;
}
.tabs-content .tabs-content-item.active {
display: block;
}
<div >
<div >
<a href="#tab3">
<img src="img/school.png">
<h3 >Instruktāžas datu aizsardzībā</h3>
</a>
</div>
</div>
<div class=tabs-content>
<div id="tab3" >
content of tab3 with self contained link: <a id="here" href="#here">here</a>. Clicking "here" should not close the tab!
</div>
</div>