I have a button that should hidden area with nice animation. It's using height
attribute to make the animation.
The issue is, when I add content to the "hiddenable" area, the content isn't viewable as it's outside the given height. And I have to clic again on "Open" to re-hide and re-open the area.
Here is a MRE :
function changeVisility(all, toCancel = null) {
if(toCancel != null)
toCancel.preventDefault();
if(!(Symbol.iterator in Object(all)))
all = [ all ];
for(var div of all) {
for(const wrapper of div.querySelectorAll(".collapse-wrapper")) {
if(wrapper.style.height == '0px') {
wrapper.style.height = wrapper.querySelector(".collapse-content").getBoundingClientRect().height + `px`;
} else {
wrapper.style.height = '0px';
}
}
}
}
for(const wrapper of document.querySelectorAll(".collapse-wrapper")) {
if(wrapper.style.height != '0px') { // set real height
wrapper.style.height = wrapper.querySelector(".collapse-content").getBoundingClientRect().height + `px`;
} else
wrapper.style.height = '0px';
}
function addContent() {
document.getElementById("contentToAdd").insertAdjacentHTML("beforeend", "<p>Other text</p>");
}
.collapse-wrapper {
overflow: hidden;
transition: height 300ms ease-in;
}
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
<body style="margin-left: 10px;">
<div class="form-check" style="padding-top: 0.5rem;">
<input class="form-check-input" type="checkbox" name="newinput" id="newinput" onchange="changeVisility(document.getElementById('input_selection'));">
<label class="form-check-label" for="newinput">
Open
</label>
</div>
<div id="input_selection">
<div style="height: 0px;" class="collapse-wrapper">
<div class="collapse-content row" id="contentToAdd" style="background-color: red;">
<button onclick="addContent()" type="button" class="btn btn-primary">Add</button>
<p>Some text</p>
</div>
</div>
</div>
</body>
To reproduce :
How can I make them be showable and keeping the nice animation ?
I tried to set height to null
to show, but there is no longer animation...
Everytime content is added you need to recalculate the new height. addContent(e)
has been changed:
function addContent(e) {
this.parentElement.insertAdjacentHTML("beforeend", "<p>Other text</p>");
const wrapper = this.closest(".collapse-wrapper");
wrapper.style.height = wrapper.querySelector(".collapse-content")
.getBoundingClientRect().height + `px`;
}
In fact I changed everything so it's no longer dependent on #id
s thus allowing you to add as many dropdowns as you like. Every <input>
needs class="toggle"
and every <button>
needs class="add"
(See Example 1). BTW the animated dropdown can be done with CSS and no JavaScript at all (see Example 2 and this article).
Example 1
function changeVisility(e) {
const wrapper = this.closest(".form-check").nextElementSibling.querySelector(".collapse-wrapper");
if (wrapper.style.height == '0px') {
wrapper.style.height = wrapper.querySelector(".collapse-content").getBoundingClientRect().height + `px`;
} else {
wrapper.style.height = '0px';
}
}
function addContent(e) {
this.parentElement.insertAdjacentHTML("beforeend", "<p>Other text</p>");
const wrapper = this.closest(".collapse-wrapper");
wrapper.style.height = wrapper.querySelector(".collapse-content").getBoundingClientRect().height + `px`;
}
document.querySelectorAll(".toggle").forEach(t => t.onchange = changeVisility);
document.querySelectorAll(".add").forEach(a => a.onclick = addContent);
.collapse-wrapper {
overflow: hidden;
transition: height 300ms ease-in;
}
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<body style="margin-left: 10px;">
<div class="form-check" style="padding-top: 0.5rem;">
<label class="form-check-label">
<input class="toggle form-check-input" type="checkbox" name="newinput">
Open
</label>
</div>
<div id="input_selection">
<div style="height: 0px;" class="collapse-wrapper">
<div class="collapse-content row" style="background-color: red;">
<button type="button" class="add btn btn-primary">Add</button>
<p>Some text</p>
</div>
</div>
</div>
</body>
Example 2
function addContent(e) {
const wrapper = this.closest(".collapse-wrapper");
wrapper.querySelector(".collapse-content").insertAdjacentHTML("beforeend", `<p>More text</p>`);
}
document.querySelectorAll(".add").forEach(a => a.onclick = addContent);
.toggle {
display: none;
}
.collapse-wrapper {
height: auto;
max-height: 0px;
overflow: hidden;
transition: max-height 0.7s ease-in;
}
.toggle:checked~.collapse-wrapper {
max-height: 100vh;
}
.form-check-label {
display: block;
width: 90%;
padding: 0 0 1ex 1ch;
text-align: center;
cursor: pointer;
}
.form-check-label::before {
content: "Open";
}
.toggle:checked+.form-check .form-check-label::before {
content: "Close";
}
.btn-block {
width: 100%
}
p {
padding: 1ex 2ch;
}
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<body>
<main class="container ml-1">
<section class="row">
<input id="t1" class="toggle" type="checkbox">
<div class="form-check col-12" style="padding-top: 0.5rem;">
<label for="t1" class="form-check-label"></label>
</div>
<div class="collapse-wrapper row">
<div class="collapse-content bg-light col-12">
<button class="add btn btn-block btn-primary" type="button">Add</button>
<p>Some text</p>
</div>
</div>
</section>
</main>
</body>