I want to animate the height of a div. This is normally done in CSS by animating the max-height
property.
However I need to do this in JS. The div is populated with dynamic content that changes frequently so the actual height is not known ahead of time.
Here is a jsfiddle: https://jsfiddle.net/vpknxuk8/
var box = document.getElementById("box");
var button = document.getElementById("button");
var expanded = true;
button.addEventListener('click', function() {
if (expanded) {
box.style.maxHeight = 0;
expanded = false;
} else {
box.style.maxHeight = "";
expanded = true;
}
});
#box {
margin-top: 20px;
border: 1px solid black;
background-color: #4f88ff;
width: 200px;
transition: max-height 0.25s linear;
overflow: hidden;
}
<button id="button">Click</button>
<div id="box">
Hello World<br>
This is dynamic content<br>
The actual height won't be known ahead of time
</div>
The box should transition between a height of 0 and its default height, but for some reason it isn't animating.
The only other versions of this question on StackOverflow pertain to CSS-only solutions or jQuery solutions.
Don't use max-height
...well, unless you want to adopt the CSS-only sloppy solution that generates delay gap issues - ...that's why you recurred to JS in the first place, hopefully
so, use height
and transition: height
instead
function slidetoggle() {
document.querySelectorAll(this.getAttribute('data-slidetoggle')).forEach(el => {
const ch = el.clientHeight,
sh = el.scrollHeight,
isCollapsed = !ch,
noHeightSet = !el.style.height;
el.style.height = (isCollapsed || noHeightSet ? sh : 0) + "px";
if (noHeightSet) return slidetoggle.call(this);
});
}
document.querySelectorAll("[data-slidetoggle]").forEach(el => el.addEventListener('click', slidetoggle));
#box1,
#box2 {
overflow: hidden;
margin-bottom: 20px;
background-color: #4f88ff;
width: 200px;
transition: height 0.5s;
}
<button data-slidetoggle="#box1">Toggle Box 1</button>
<div id="box1">
Hello World<br> This is dynamic content<br> The actual height won't be<br> known ahead of time
</div>
<button data-slidetoggle="#box2">Toggle Box 2</button>
<div id="box2">
Hello World<br> This is dynamic content<br> The actual height won't be<br> known ahead of time<br> bla
<br> bla
<br> bla
</div>
TIP: if you need paddings, my suggestion is to wrap your content into another child box - with paddings.
The code above is a conceptual and minimal - with lots of room for improvements, but hopefully you got the idea.
CSS height-transition cannot work if we don't set an initial height.
So basically on click - if such height does not exists we need to set it via JS.
After such height is set the transition will still not work because the browser did not had the time to calculate the box model of such element - to transition-from:
// (INVALID DEMO)
//
// Say the element is expanded.
// We need a height to transition-from
el.height = calculatedHeight +"px"; // set CSS height to the calculated value
// Transition to 0px height....
el.height = 0 +"px";
// NO DEAL. our element just snapped to 0 without transition
therefore we need to reside to some kind of callback.
One type of common (but sloppy way) is to use a setTimeout()
callback
// (INVALID DEMO)
//
// Say the element is expanded.
// We need a height to transition-from
el.height = calculatedHeight +"px"; // set CSS height to the calculated value
// Transition to 0px height giving the browser some ready-state hack
setTimeout(function() {
// Eventually the box model will be calculated
el.height = 0 +"px";
}, 0); // <<< PROBLEM: Should we use 0 ? 10? 100? 1000?
// and our element beautifully transitioned to 0 height.
// Eventually :(
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With