Why is this working:
const details = document.querySelector('details')
document.getElementById("details1").addEventListener("toggle", event => {
if (details1.open) {
changeText1("open");
} else {
changeText1("closed");
}
});
document.getElementById("details2").addEventListener("toggle", event => {
if (details2.open) {
changeText2("open");
} else {
changeText2("closed");
};
});
function changeText1(status) {
if(status == "open") {
document.getElementById("p1").innerText = "opened";
} else if (status == "closed") {
document.getElementById("p1").innerText = "closed";
}
}
function changeText2(status) {
if(status == "open") {
document.getElementById("p2").innerText = "opened";
} else if (status == "closed") {
document.getElementById("p2").innerText = "closed";
}
}
<html>
<body>
<details id="details1">
<summary>#1</summary>text
</details>
<details id="details2">
<summary>#2</summary>text
</details>
<p id="p1">not opened or closed</p>
<p id="p2">not opened or closed</p>
</body>
</html>
And is this not working:
const details = document.querySelector('details')
function changeText(detailnr, status) {
let pnr = "p" detailnr;
if(status == "open") {
document.getElementById(pnr).innerText = "opened";
} else {
document.getElementById(pnr).innerText = "closed";
}
}
function maaklistner(detailsnr){
let detailsid = "details" detailsnr;
document.getElementById(detailsid).addEventListener("toggle", event => {
if (detailsid.open) {
changeText(detailsnr, "open");
} else {
changeText(detailsnr, "closed");
}
});
}
for(let i = 1; i < 3; i ){
maaklistner(i);
}
<html>
<body>
<details id="details1">
<summary>#1</summary>text
</details>
<details id="details2">
<summary>#2</summary>text
</details>
<p id="p1">not opened or closed</p>
<p id="p2">not opened or closed</p>
</body>
</html>
While the code in essence is exactly a duplicate...
Seems like the eventlistener itself IS created. But, the details.opened statement IS NOT treated correctly at the seconde code, while the same methode does work at the first code.
Is there a way to get the second method working? Since I need to create more then 2000 of them...
CodePudding user response:
It's not working because you use detailsid to check if the details element is open. You need to use the elements that contain the id detailsid.
Just replace detailsid.open with document.getElementById(detailsid).open.
Here is an example that is working:
const details = document.querySelector('details')
function changeText(detailnr, status) {
let pnr = "p" detailnr;
if (status == "open") {
document.getElementById(pnr).innerText = "opened";
} else {
document.getElementById(pnr).innerText = "closed";
}
}
function maaklistner(detailsnr) {
let detailsid = "details" detailsnr;
document.getElementById(detailsid).addEventListener("toggle", event => {
if (document.getElementById(detailsid).open) {
changeText(detailsnr, "open");
} else {
changeText(detailsnr, "closed");
}
});
}
for (let i = 1; i < 3; i ) {
maaklistner(i);
}
<details id="details1">
<summary>#1</summary>text
</details>
<details id="details2">
<summary>#2</summary>text
</details>
<p id="p1">not opened or closed</p>
<p id="p2">not opened or closed</p>
CodePudding user response:
add document.getElementById(detailsid) in place of detailsid
const details = document.querySelector('details')
function changeText(detailnr, status) {
let pnr = "p" detailnr;
if (status == "open") {
document.getElementById(pnr).innerText = "opened";
} else {
document.getElementById(pnr).innerText = "closed";
}
}
function maaklistner(detailsnr) {
let detailsid = "details" detailsnr;
document.getElementById(detailsid).addEventListener("toggle", event => {
if (document.getElementById(detailsid).open) {
changeText(detailsnr, "open");
} else {
changeText(detailsnr, "closed");
}
});
}
for (let i = 1; i < 3; i ) {
maaklistner(i);
}
<details id="details1">
<summary>#1</summary>text
</details>
<details id="details2">
<summary>#2</summary>text
</details>
<p id="p1">not opened or closed</p>
<p id="p2">not opened or closed</p>
CodePudding user response:
I would suggest you to forget about unique ids here... You can set an event listener for each details element using a forEach loop.
A data-* attribute will hold the index (zero-based) of the p to target for text change.
Have a look! It is much simpler.
// Get all the details elements
let details = document.querySelectorAll("details");
// Get an array from all the p elements
let stateIndicators = Array.from(document.querySelectorAll(".stateWrapper p"))
// For each details element, set an event listener
details.forEach(function(detail) {
detail.addEventListener("toggle", function(e) {
// Get its index from the data attribute
let index = this.dataset.index
// Change the text based on the open state
stateIndicators[index].innerText = this.open ? "opened" : "closed"
})
})
<html>
<body>
<details data-index="0">
<summary>#1</summary>text
</details>
<details data-index="1">
<summary>#2</summary>text
</details>
<details data-index="2">
<summary>#3</summary>text
</details>
<details data-index="3">
<summary>#4</summary>text
</details>
<details data-index="4">
<summary>#5</summary>text
</details>
<div ><!-- usefull wrapper here -->
<p>not opened or closed</p>
<p>not opened or closed</p>
<p>not opened or closed</p>
<p>not opened or closed</p>
<p>not opened or closed</p>
</div>
</body>
</html>
CodePudding user response:
Use "click" event since "toggle" doesn't bubble. Event bubbling is needed to use Event Delegation which allows you to add a single event handler to listen for events for an unlimited amounr of tags including dynamically added tags.
const flipDetails = event => {
const parent = event.currentTarget;
const clicked = event.target;
const nodes = [...parent.children];
const log = document.querySelector('output');
if (clicked !== parent && clicked.matches('summary')) {
let dtl = clicked.parentElement;
let pos = nodes.indexOf(dtl) 1;
let status = dtl.open === true ? `${pos}: Closed` : `${pos}: Open`;
log.innerHTML = '<br>' status;
}
}
const main = document.querySelector('main');
main.addEventListener("click", flipDetails);
// For demo only
document.querySelector('output').insertAdjacentHTML('beforeBegin', `
<details>
<summary>Added Dynamically</summary>text
</details>`);
output::before {content: 'Status: '}
<html>
<body>
<main>
<details>
<summary>#1</summary>text
</details>
<details>
<summary>#2</summary>text
</details>
<output></output>
</main>
</body>
</html>
