I think I'm very close to the solution. I have created a function in Javascript to open and close tabs on click. I have successfully achieved being able to open and close them on click, but I would like to close the tab only when I'm clicking on its header. Any help on how I could do this?
const openCategories = () => {
const categories = document.querySelectorAll('.single-category');
categories.forEach((category) => {
category.addEventListener('click', () => {
category.classList.toggle('opened');
});
});
}
openCategories();
<div id="store-categories">
<div id="category-header">
<span>Categories</span>
<div id="toggle-all-categories"><span>X</span></div>
</div>
<div id="category-all">
<div >
<div >
<h3>first category</h3>
<div ><span>X</span></div>
</div>
<div >
[My items]
</div>
</div>
<div >
<div >
<h3>Second category</h3>
<div ><span>X</span></div>
</div>
<div >
[My items]
</div>
</div>
</div>
CodePudding user response:
Pass the Event in your click handler arguments and use Event.target.closest("selector")
The Event.target
is the exact element that dispatched the event, which might also be a child of the event delegator. In combination with Element.closest("selector") method you can check if either a closest ancestor / or self — match that specific selector. If such an element is returned, the if
will trigger its code in body.
const categories = document.querySelectorAll('.single-category');
categories.forEach((category) => {
category.addEventListener('click', (evt) => {
if (evt.target.closest(".single-category-header")) {
category.classList.toggle('opened');
}
});
});
.single-category .content { display: none; }
.single-category.opened .content { display: block; }
<div id="category-all">
<div >
<div ><h3>first category</h3></div>
<div >111</div>
</div>
<div >
<div ><h3>Second category</h3></div>
<div >222</div>
</div>
</div>
Yet better, delegate your events from the parent element:
document.querySelector("#category-all").addEventListener("click", (evt) => {
const elHeader = evt.target.closest(".single-category-header");
if (!elHeader) return; // Not a header, do nothing
elHeader.closest(".single-category").classList.toggle("opened");
});
.single-category .content { display: none; }
.single-category.opened .content { display: block; }
<div id="category-all">
<div >
<div >
<h3>first category</h3>
</div>
<div >111</div>
</div>
<div >
<div >
<h3>Second category</h3>
</div>
<div >222</div>
</div>
</div>
CodePudding user response:
Please delegate
document.getElementById("store-categories").addEventListener('click', (e) => {
const tgt = e.target;
if (!tgt.matches("h3") ) return; // not a header
const isSingle = tgt.closest("div.single-category");
if (isSingle) isSingle.querySelector(".content").classList.toggle('opened');
});
document.getElementById("toggle-all-categories").addEventListener("click", e => {
document.querySelectorAll(".content").forEach(content => content.classList.remove("opened"));
})
.single-category .content { display:none }
.single-category .content.opened { display:block}
<div id="store-categories">
<div id="category-header">
<span>Categories</span>
<div id="toggle-all-categories"><span>X</span></div>
</div>
<div id="category-all">
<div >
<div >
<h3>first category</h3>
</div>
<div >
[My items]
</div>
</div>
<div >
<div >
<h3>Second category</h3>
</div>
<div >
[My items]
</div>
</div>
</div>
CodePudding user response:
Or add on-other onClick
on the header
which toggles the openend
class on it's parentNode
const openCategories = () => {
document.querySelectorAll('.single-category').forEach((category) => {
category.addEventListener('click', () => {
category.classList.toggle('opened');
});
category.querySelector('.single-category-header').onClick = () => {
event.target.parentNode.classList.toggle('opened');
};
});
}
openCategories();
.single-category .content {
display: none;
}
.single-category.opened .content {
display: block;
}
<div id="store-categories">
<div id="category-header">
<span>Categories</span>
<div id="toggle-all-categories"><span>X</span></div>
</div>
<div id="category-all">
<div >
<div >
<h3>first category</h3>
<div ><span>X</span></div>
</div>
<div >
[My items]
</div>
</div>
<div >
<div >
<h3>Second category</h3>
<div ><span>X</span></div>
</div>
<div >
[My items]
</div>
</div>
</div>