Reputation: 313
I'm trying to make an animation for sliding up and sliding down. To toggle parts of the page. When the height is "hardcoded" in CSS to 210px before rendering the page and I call the Javascript function from a button it all works.
But when I try to do this dynamically, with Javascript to keep "hardcoding" to a minimum. It still does the change to the height. But the transition effect does not happen.
Here is the code snippet to high light the problem. I don't understand as to why this difference sabotages the transition.
function slideUp() {
var target = document.getElementById("targetDiv");
target.style.height = "" + target.clientHeight+"px"; // taking the rendered height of the div and setting it in CSS to mimic the pre set height in CSS
target.style.transition = "height 1.0s ease-in 0s";
target.style.height = "0px";
}
.divStyle {
/* height: 200px; without this the animation does not work */
background: blue;
overflow: hidden;
}
<div id="targetDiv" class="divStyle">
random content
</div>
<button onclick="slideUp()"> Slide up </button>
Upvotes: 3
Views: 2237
Reputation: 53
As mentioned before: having a height/max-height value set to auto/none stops the transition from working.
Rather than setting the max-height to a specified, larger than needed
max-height
by guesswork (if set to really high number causes a delay in the
transition), I added transition and a max-height to the content wrapper instead,
and used JavaScript to read the actual height of the contents. This way the
wrapper max-height
is set to the currently rendered height of its contents.
This gives it a fixed inline height.
The problem with this solution, is that if the user decides to resize his
window, into say, a narrower screen (i.e. rotating from landscape to portrait
mode on mobile), the fixed max-height causes to content to be cut-off. The
work-around for this is setting a setTimeout()
function to set the max-height
of the wrapper back to none
just after the transition has ended. (none
is
the default, initial CSS value of the max-height
property)
The same thing needs to happen before the "contracting" transition. The wrapper's max-height needs to be a fixed value for the transition to work. Therefore, the height needs to again be read from its contents (which might have actually changed in the meantime), applied to the wrapper, then after a short delay (50ms seemed to work in my case, and the delay wasn't noticeable) begin the transition.
Here's an example that expands the div contents when a button is pressed:
HTML:
<div id="content-wrapper">
<div id="content">
<!-- a lot of text content -->
</div>
</div>
<button id="show-more" type="button">Show More</button>
CSS:
#content-wrapper {
/* Initial max-height, can be any value including 0 */
max-height: 270px;
overflow: hidden;
/* transition occurs on max-height change with specified delay; */
transition: max-height 0.7s;
}
#content {
/* This makes margin of the contents be respected by the container
i.e. the height of the container its childrens' margins */
overflow: hidden;
}
JS:
function resetHeight() {
// make max-height of wrapper responsive again
wrapper.style.maxHeight = "none";
}
function toggleText() {
// check if content-wrapper is open
if (wrapper.className.includes("open")) {
// if is open, close content-wrapper and set initial max-height to wrapper
let contentHeight = window.getComputedStyle(content).height;
// sets max-height back from none to computed content height
wrapper.style.maxHeight = contentHeight;
// give it some time to reset the transition trigger caused by previous statement
setTimeout(function() {
wrapper.classList.remove("open");
wrapper.style.maxHeight = "270px"; // reset to our initial max-height
button.textContent = "Show More";
}, 50)
} else {
// if not open, open content-wrapper then use content's height to set the
// max-height of wrapper
wrapper.classList.add("open");
let contentHeight = window.getComputedStyle(content).height;
wrapper.style.maxHeight = contentHeight;
button.textContent = "Show Less";
setTimeout(resetHeight, 700) // the timeout is the same as the transition time
}
}
const content = document.getElementById("content")
const wrapper = document.getElementById("content-wrapper");
const button = document.getElementById("show-more");
button.addEventListener("click", toggleText);
This is my first answer on StackOverflow, so if there is anything I can improve please do let me know.
Upvotes: 0
Reputation: 313
Just so that if anyone comes along here, the given answers are good, just not in the current case I need. The way I managed finally is as follows.
function slideDown( targetId ){
var target = document.getElementById(targetId);
target.style.height = target.children[0].clientHeight + "px";
}
function slideUp( targetId ) {
var target = document.getElementById(targetId);
target.style.height = "0px";
}
.parentDivStyle{
overflow: hidden;
transition: height 0.5s ease-in 0s;
height: 0px;
background: blue;
}
.childDivStyle {
background: green;
padding: 10px 10px 10px 10px;
}
<div id="parent" class="parentDivStyle">
<div class="childDivStyle">
random stuff
text text text...........
</div>
</div>
<button onclick="slideUp('parent')"> Slide Up </button>
<button onclick="slideDown('parent')"> Slide Down </button>
This was actually a comment in a deleted answer. Idk why it was deleted. Was useful.
Upvotes: 1
Reputation: 2211
There are multiple ways to achieve the goal, in your case you just require to init the style which is supposed to change at the later time so just use window.onload
and init the value.
var org = "";
window.onload = function() {
var target = document.getElementById("targetDiv");
org = target.clientHeight;
target.style.height = "" + target.clientHeight + "px";
}
function slideUp() {
var target = document.getElementById("targetDiv");
target.style.transition = "height 1.0s ease-in 0s";
if (target.clientHeight == org) {
target.style.height = "0px";
} else {
target.style.height = org + "px";
}
}
.divStyle {
/* height: 200px; without this the animation does not work */
background: blue;
overflow: hidden;
}
<div id="targetDiv" class="divStyle">
random content
</div>
<button onclick="slideUp()"> Slide up </button>
Upvotes: 1
Reputation: 11291
Use of max-height
combined with setTimeout
"hack" worked for me:
function slideUp() {
var target = document.getElementById("targetDiv");
target.style.maxHeight = target.clientHeight + "px";
setTimeout(function() {
target.style.maxHeight = 0;
}, 10);
}
.divStyle {
background: blue;
overflow: hidden;
transition: max-height 1s ease-in 0s;
}
<div id="targetDiv" class="divStyle">
random content
</div>
<button onclick="slideUp()"> Slide up </button>
However, it is not a clean way. Think of using transform: scaleY(0)
instead.
Upvotes: 3