I have 3 buttons and 3 content. When I click on any button, I want to show the content of that button. When I click on the other button, I want to show the content of that button and delete the content of the other button.
I managed to do this with simple logic, but how can I do it with a for loop?
const content = document.querySelectorAll('.content');
const contentClose = document.querySelector('.close-content');
const btnsOpenContent = document.querySelectorAll('.show-content');
btnsOpenContent[0].addEventListener('click',
function() {
content[1].classList.remove('show-modal');
content[2].classList.remove('show-modal');
content[0].classList.add('show-modal');
}
)
btnsOpenContent[1].addEventListener('click',
function() {
content[0].classList.remove('show-modal');
content[2].classList.remove('show-modal');
content[1].classList.add('show-modal');
}
)
btnsOpenContent[2].addEventListener('click',
function() {
content[0].classList.remove('show-modal');
content[1].classList.remove('show-modal');
content[2].classList.add('show-modal');
}
)
CodePudding user response:
Use simple forEach
loops along with classList.toggle(className: string, force: boolean)
:
const content = document.querySelectorAll('.content');
const contentClose = document.querySelector('.close-content');
const btnsOpenContent = document.querySelectorAll('.show-content');
btnsOpenContent.forEach((btn, index) => {
btn.addEventListener('click', () => {
content.forEach((cnt, idx) => cnt.classList.toggle('show-modal', idx === index))
})
})
CodePudding user response:
I strongly suggest to delegate, which means instead of adding a listener to each and every button, you instead capitalize on the bubbling mechanism of events, listening to clicks instead on just one common ancestor element of all buttons. Then inside the click handler you implement a guard that checks if the click actually came from a button.
Note I added hidden to the last two content to start with content 1
You can also add active to the button that was clicked and remove from the siblings
NOTE: This code does not change no matter how many buttons as long as there is a matching number of content divs.
Also note there is no need for a class to show and hide
Lastly note I implemented your close too
const nav = document.getElementById("nav");
const contents = document.getElementById("contents");
const buttons = nav.querySelectorAll("#nav .show-content");
const contentDivs = contents.querySelectorAll("div.content")
nav.addEventListener("click", e => {
const tgt = e.target; // what was clicked?
if (!tgt.matches(".show-content")) return; // not a button
buttons.forEach((but,i) => contentDivs[i].hidden = but !== tgt); // hide if not the content that belongs to button
})
contents.addEventListener("click", e => {
const tgt = e.target;
if (!tgt.matches(".close")) return; // not a button
tgt.closest("div").hidden = true; // hide the div the close button is in
})
<div id="nav">
<button >Show 1</button>
<button >Show 2</button>
<button >Show 3</button>
</div>
<div id="contents">
<div >Content 1 <button >X</button></div>
<div hidden>Content 2 <button >X</button></div>
<div hidden>Content 3 <button >X</button></div>
</div>
CodePudding user response:
The most important thing to note is that querySelectorAll does not return an array, and instead returns a NodeList. ES6 has introduced a handy method that is intended for this exact purpose NodeList.prototype.forEach().
The easiest approach in my experience to create tabbed content is to add some sort of identifier for each ".content" tab on the button triggering the event. the "data" attribute is often used for this, however, there several other options.
An example of your button html using the data attribute would look like the following:
<button data-content="content1">Show content 1</button>
<button data-content="content2">Show content 2</button>
<button data-content="content3">Show content 3</button>
For this technique your content HTML would need an id that matches the identifier used on your buttons:
<div id="content1">Some content for 1</div>
<div id="content2">Some content for 2</div>
<div id="content3">Some content for 3</div>
And the corresponding javascript would look similar to below:
const btnsOpenContent = document.querySelectorAll('.show-content');
btnsOpenContent.forEach((button) => {
button.addEventListener('click', (event) => {
let contentId = event.target.getAttribute('data-content');
let targetContent = document.getElementById(contentId);
// Hide all contents
document.querySelectorAll('content').forEach((element) => {
element.classList.remove('show-modal');
})
// Show selected content
targetContent.classList.add('show-modal');
});
});
This code can even be shortened:
const btnsOpenContent = document.querySelectorAll('.show-content');
btnsOpenContent.forEach((button) => {
button.addEventListener('click', (event) => {
let contentId = event.target.dataset.content;
let targetContent = document.getElementById(contentId);
document.querySelectorAll('content').forEach((element) => {
element.classList.toggle('show-modal', contentId === element.id);
})
});
});
CodePudding user response:
Maybe something like this?
const content = document.querySelectorAll('.content');
const contentClose = document.querySelector('.close-content');
const btnsOpenContent = document.querySelectorAll('.show-content');
for (let i = 0; i <= 2; i ) {
btnsOpenContent[i].onclick = () => {
for (let j = 0; j <= 2; j ) {
i == j ? content[j].classList.add('show-modal') : content[j].classList.remove('show-modal');
}
};
}