I have the following code
const collapse = (elem,forceClose) => {
const content = elem.nextElementSibling;
if (!content || !content.matches(".content")) return; // no content after the element, so leave
if (forceClose) elem.classList.remove("active"); // passed force or nothing passed
else elem.classList.toggle("active");
content.style.maxHeight = elem.classList.contains("active") ? content.scrollHeight "px" : null;
};
const buttons = document.querySelectorAll(".collapsible");
document.getElementById("container").addEventListener("click", e => {
const tgt = e.target;
if (!tgt.matches(".collapsible")) return; // clicked on something not a button
if (tgt.matches(".all")) { // special button
const close = container.querySelectorAll(".collapsible.active").length > 0;
buttons.forEach(but => collapse(but,close));
} else {
collapse(tgt);
}
});
.content {
max-height:0;
overflow:hidden;
}
<div id="container">
<button >XXXXXXXXXX <u>YYYYYYYYYY</u></button>
<div style="">Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</div>
</div>
Click on the XXXXXXXXXX
on the button to hide and show the content. This is working as expected.
But if you click on YYYYYYYYYY
it's not working, just because YYYYYYYYYY
is underlined.
Why doesn't the buttons function not work any more just because of the underlined text? How can I solve this? I need the button to work and the text underlined.
CodePudding user response:
It's because e.target
points to the exact element that triggered the handler when the event bubbles up, in that case the, <u></u>
node not the button.
Think of it this way, the only reason the event fires even though you added the listener to the container, not the button is because the event bubbles up, finds the listener on one of the parents, and triggers the handler.
I do think you should add the event handler to the button itself instead of the container, but if you need it to stay as it is, you can check the event.composedPath()
which has all the elements/objects that the event bubbles through and then check if that path contains the button.
If you decide to add the handler to the button you'll also need to use e.currentTarget
which references the element which the handler callback is assigned to. So that even if you click on <u></u>
, when the event bubbles to the button, currentTarget
will reference the button itself.
Here's a solution where I kept your approach as it as, but used composedPath()
instead of event.target
const collapse = (elem,forceClose) => {
const content = elem.nextElementSibling;
if (!content || !content.matches(".content")) return; // no content after the element, so leave
if (forceClose) elem.classList.remove("active"); // passed force or nothing passed
else elem.classList.toggle("active");
content.style.maxHeight = elem.classList.contains("active") ? content.scrollHeight "px" : null;
};
const buttons = document.querySelectorAll(".collapsible");
document.getElementById("container").addEventListener("click", e => {
const pathArray = [...e.composedPath()]
console.log('Event Target: ', e.target)
// console.log(pathArray); // This is a bit laggy in the snippet, but uncomment it if you want to see what the even path looks like.
const collapseButton = pathArray.find(obj => obj?.classList?.contains('collapsible'))
const allButton = pathArray.find(obj => obj?.classList?.contains('all'))
if (!collapseButton && !allButton) return; // clicked on something not a button
if (allButton) { // special button
const close = container.querySelectorAll(".collapsible.active").length > 0;
buttons.forEach(but => collapse(but,close));
} else {
collapse(collapseButton);
}
});
.content {
max-height: 0;
overflow: hidden;
}
<div id="container">
<button >XXXXXXXXXX <u>YYYYYYYYYY</u></button>
<div style="">Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</div>
</div>
CodePudding user response:
You're wrapping YYYYYYYYYY
into an <u>
tag, so when you click on it, the onClick
event of <u>
tag was called, not <button>
. Try it with <div>
or <nav>
,... will return the same effect.