I created function which create elements
function createElements(value) {
//Create div .toggler
const toggler = document.createElement('div');
toggler.classList.add('toggler');
//Create div .content
const content = document.createElement('div');
content.classList.add('content');
//Append div .toggler in div .container
container.append(toggler);
//Append div .content in div .container
container.append(content);
toggler.innerText = 'Click me';
content.innerText = value;
}
And function which should show and hide content if I’ll click toggler ‘Click me’
function toggleContent() {
//Get created elements
const togglers = document.querySelectorAll('.toggler'),
contents = document.querySelectorAll('.content');
//Toggle both div .active
togglers.forEach((toggler, index) => {
const content = contents[index];
//Get div .content height
const contentHeight = content.clientHeight + 'px';
//Set div .content height
content.style.height = '0px';
//Toggle event
toggler.addEventListener('click', () => {
if (!toggler.classList.contains('active')) {
toggler.classList.add('active');
} else {
toggler.classList.remove('active');
}
if (!content.classList.contains('active')) {
content.classList.add('active');
content.style.height = contentHeight;
} else {
content.classList.remove('active');
content.style.height = '0px';
}
});
});
}
Put these both functions in event of button
//Create html elements and set value inner them event
btn.addEventListener('click', () => {
createElements(input.value);
toggleContent();
//Clear input value
input.value = '';
// console.log(input.value);
});
All works fine, but if I add second element, first element toggler does not work
//Get html elements
const container = document.querySelector('.container'),
input = document.querySelector('.input'),
btn = document.querySelector('.btn');
function createElements(value) {
//Create div .toggler
const toggler = document.createElement('div');
toggler.classList.add('toggler');
//Create div .content
const content = document.createElement('div');
content.classList.add('content');
//Append div .toggler in div .container
container.append(toggler);
//Append div .content in div .container
container.append(content);
toggler.innerText = 'Click me';
content.innerText = value;
}
function toggleContent() {
//Get created elements
const togglers = document.querySelectorAll('.toggler'),
contents = document.querySelectorAll('.content');
//Toggle both div .active
togglers.forEach((toggler, index) => {
const content = contents[index];
//Get div .content height
const contentHeight = content.clientHeight + 'px';
//Set div .content height
content.style.height = '0px';
//Toggle event
toggler.addEventListener('click', () => {
if (!toggler.classList.contains('active')) {
toggler.classList.add('active');
} else {
toggler.classList.remove('active');
}
if (!content.classList.contains('active')) {
content.classList.add('active');
content.style.height = contentHeight;
} else {
content.classList.remove('active');
content.style.height = '0px';
}
});
});
}
//Create html elements and set value inner them event
btn.addEventListener('click', () => {
createElements(input.value);
toggleContent();
//Clear input value
input.value = '';
// console.log(input.value);
});
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.content {
overflow: hidden;
}
<input type="text" class="input" />
<button class="btn">add</button>
<div class="container">
<!-- <div class="toggler"></div>
<div class="content"></div> -->
</div>
>Solution :
The first toggler (and subsequent ones as you add more elements) stop working because of 2 reasons:
- every time a new element is added you add another click listener to the existing elements (so when 2nd element is added, 1st element gets another click listener, this causes some weirdness later on)
- every time a new element is added for every existing item you remember their
clientHeightso if they are actually in a collapsed state, you will store0pxtocontentHeightso the new click handler will then be always using0pxalso for expanded state height. It would be better to toggledisplay: noneanddisplay: block(or inline or inline-block whichever you need)
I moved the click handler registration to the first function, in this way every created element gets only one click handler.
I removed the clientHeight/contentHeight logic and just added a CSS rule so that .content items are visible when .active and not visible otherwise.
So the change was this part in CSS
.content {
display: none;
}
.content.active {
display: block;
}
I kept the logic that when you add a new item it will collapse all existing items. I think that is what you had in you logic too, but let me know if you don’t want that.
The change was in JS code
//Toggle both div .active
togglers.forEach((toggler, index) => {
const content = contents[index];
// I added this to keep the same logic you had before
// if you would like to not collapse them, just remove the 2 lines
toggler.classList.remove('active');
content.classList.remove('active');
});
//Get html elements
const container = document.querySelector('.container'),
input = document.querySelector('.input'),
btn = document.querySelector('.btn');
function createElements(value) {
//Create div .toggler
const toggler = document.createElement('div');
toggler.classList.add('toggler');
//Create div .content
const content = document.createElement('div');
content.classList.add('content');
//Append div .toggler in div .container
container.append(toggler);
//Append div .content in div .container
container.append(content);
toggler.innerText = 'Click me';
content.innerText = value;
toggler.addEventListener('click', () => {
if (!toggler.classList.contains('active')) {
toggler.classList.add('active');
} else {
toggler.classList.remove('active');
}
if (!content.classList.contains('active')) {
content.classList.add('active');
} else {
content.classList.remove('active');
}
});
}
function toggleContent() {
//Get created elements
const togglers = document.querySelectorAll('.toggler'),
contents = document.querySelectorAll('.content');
//Toggle both div .active
togglers.forEach((toggler, index) => {
const content = contents[index];
toggler.classList.remove('active');
content.classList.remove('active');
});
}
//Create html elements and set value inner them event
btn.addEventListener('click', () => {
createElements(input.value);
toggleContent();
//Clear input value
input.value = '';
// console.log(input.value);
});
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.content {
display: none;
}
.content.active {
display: block;
}
<input type="text" class="input" />
<button class="btn">add</button>
<div class="container">
<!-- <div class="toggler"></div>
<div class="content"></div> -->
</div>