CSS 我如何过渡高度:0;到高度:自动;使用CSS?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3508605/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How can I transition height: 0; to height: auto; using CSS?
提问by Hailwood
I am trying to make a <ul>
slide down using CSS transitions.
我正在尝试<ul>
使用 CSS 转换向下滑动。
The <ul>
starts off at height: 0;
. On hover, the height is set to height:auto;
. However, this is causing it to simply appear, nottransition,
将<ul>
在开始关闭height: 0;
。悬停时,高度设置为height:auto;
。然而,这导致它只是出现,而不是过渡,
If I do it from height: 40px;
to height: auto;
, then it will slide up to height: 0;
, and then suddenly jump to the correct height.
如果我从height: 40px;
到height: auto;
,那么它会向上滑动到height: 0;
,然后突然跳到正确的高度。
How else could I do this without using JavaScript?
如果不使用 JavaScript,我还能如何做到这一点?
#child0 {
height: 0;
overflow: hidden;
background-color: #dedede;
-moz-transition: height 1s ease;
-webkit-transition: height 1s ease;
-o-transition: height 1s ease;
transition: height 1s ease;
}
#parent0:hover #child0 {
height: auto;
}
#child40 {
height: 40px;
overflow: hidden;
background-color: #dedede;
-moz-transition: height 1s ease;
-webkit-transition: height 1s ease;
-o-transition: height 1s ease;
transition: height 1s ease;
}
#parent40:hover #child40 {
height: auto;
}
h1 {
font-weight: bold;
}
The only difference between the two snippets of CSS is one has height: 0, the other height: 40.
<hr>
<div id="parent0">
<h1>Hover me (height: 0)</h1>
<div id="child0">Some content
<br>Some content
<br>Some content
<br>Some content
<br>Some content
<br>Some content
<br>
</div>
</div>
<hr>
<div id="parent40">
<h1>Hover me (height: 40)</h1>
<div id="child40">Some content
<br>Some content
<br>Some content
<br>Some content
<br>Some content
<br>Some content
<br>
</div>
</div>
回答by jake
Use max-height
in the transition and not height
. And set a value on max-height
to something bigger than your box will ever get.
max-height
在过渡中使用而不是height
. 并max-height
为比你的盒子更大的东西设置一个值。
See JSFiddle demoprovided by Chris Jordan in another answerhere.
请参阅Chris Jordan 在此处的另一个答案中提供的JSFiddle 演示。
#menu #list {
max-height: 0;
transition: max-height 0.15s ease-out;
overflow: hidden;
background: #d5d5d5;
}
#menu:hover #list {
max-height: 500px;
transition: max-height 0.25s ease-in;
}
<div id="menu">
<a>hover me</a>
<ul id="list">
<!-- Create a bunch, or not a bunch, of li's to see the timing. -->
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
</ul>
</div>
回答by dotnetCarpenter
You should use scaleY instead.
您应该改用 scaleY。
ul {
background-color: #eee;
transform: scaleY(0);
transform-origin: top;
transition: transform 0.26s ease;
}
p:hover ~ ul {
transform: scaleY(1);
}
<p>Hover This</p>
<ul>
<li>Coffee</li>
<li>Tea</li>
<li>Milk</li>
</ul>
I've made a vendor prefixed version of the above code on jsfiddle, and changed your jsfiddleto use scaleY instead of height.
回答by robertc
You can't currently animate on height when one of the heights involved is auto
, you have to set two explicit heights.
当所涉及的高度之一为 时auto
,您目前无法在高度上设置动画,您必须设置两个明确的高度。
回答by Steven Vachon
The solution that I've always used was to first fade out, then shrink the font-size
, padding
and margin
values. It doesn't look the same as a wipe, but it works without a static height
or max-height
.
我一直使用的解决方案是先淡出,然后缩小font-size
,padding
和margin
值。它看起来与擦拭不同,但它可以在没有静态height
或max-height
.
Working example:
工作示例:
/* final display */
#menu #list {
margin: .5em 1em;
padding: 1em;
}
/* hide */
#menu:not(:hover) #list {
font-size: 0;
margin: 0;
opacity: 0;
padding: 0;
/* fade out, then shrink */
transition: opacity .25s,
font-size .5s .25s,
margin .5s .25s,
padding .5s .25s;
}
/* reveal */
#menu:hover #list {
/* unshrink, then fade in */
transition: font-size .25s,
margin .25s,
padding .25s,
opacity .5s .25s;
}
<div id="menu">
<b>hover me</b>
<ul id="list">
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
</ul>
</div>
<p>Another paragraph...</p>
回答by balpha
I know this is the thirty-somethingth answer to this question, but I think it's worth it, so here goes. This is a CSS-onlysolution with the following properties:
我知道这是这个问题的 30 多岁的答案,但我认为这是值得的,所以就这样吧。这是具有以下属性的纯 CSS解决方案:
- There is no delay at the beginning, and the transition doesn't stop early. In both directions (expanding and collapsing), if you specify a transition duration of 300ms in your CSS, then the transition takes 300ms, period.
- It's transitioning the actual height (unlike
transform: scaleY(0)
), so it does the right thing if there's content after the collapsible element. - While (like in other solutions) there aremagic numbers (like "pick a length that is higher than your box is ever going to be"), it's not fatal if your assumption ends up being wrong. The transition may not look amazing in that case, but before and after the transition, this is not a problem: In the expanded (
height: auto
) state, the whole content always has the correct height (unlike e.g. if you pick amax-height
that turns out to be too low). And in the collapsed state, the height is zero as it should.
- 开始没有延迟,过渡不会提前停止。在两个方向(展开和折叠),如果您在 CSS 中指定 300 毫秒的过渡持续时间,则过渡需要 300 毫秒,周期。
- 它正在转换实际高度(与 不同
transform: scaleY(0)
),因此如果可折叠元素之后有内容,它会做正确的事情。 - 虽然(就像在其他解决方案中一样)有一些神奇的数字(比如“选择一个比你的盒子要长的长度”),但如果你的假设最终是错误的,这并不是致命的。在这种情况下,过渡可能看起来并不惊人,但是在过渡之前和之后,这不是问题:在展开 (
height: auto
) 状态下,整个内容始终具有正确的高度(与例如,如果您选择一个max-height
结果为太低)。在折叠状态下,高度应该为零。
Demo
演示
Here's a demo with three collapsible elements, all of different heights, that all use the same CSS. You might want to click "full page" after clicking "run snippet". Note that the JavaScript only toggles the collapsed
CSS class, there's no measuring involved. (You could do this exact demo without any JavaScript at all by using a checkbox or :target
). Also note that the part of the CSS that's responsible for the transition is pretty short, and the HTML only requires a single additional wrapper element.
这是一个演示,其中包含三个高度不同的可折叠元素,它们都使用相同的 CSS。单击“运行代码段”后,您可能希望单击“整页”。请注意,JavaScript 仅切换collapsed
CSS 类,不涉及测量。(通过使用复选框或 ,您可以在完全没有任何 JavaScript 的情况下完成这个精确的演示:target
)。另请注意,负责转换的 CSS 部分非常短,HTML 只需要一个额外的包装元素。
$(function () {
$(".toggler").click(function () {
$(this).next().toggleClass("collapsed");
$(this).toggleClass("toggled"); // this just rotates the expander arrow
});
});
.collapsible-wrapper {
display: flex;
overflow: hidden;
}
.collapsible-wrapper:after {
content: '';
height: 50px;
transition: height 0.3s linear, max-height 0s 0.3s linear;
max-height: 0px;
}
.collapsible {
transition: margin-bottom 0.3s cubic-bezier(0, 0, 0, 1);
margin-bottom: 0;
max-height: 1000000px;
}
.collapsible-wrapper.collapsed > .collapsible {
margin-bottom: -2000px;
transition: margin-bottom 0.3s cubic-bezier(1, 0, 1, 1),
visibility 0s 0.3s, max-height 0s 0.3s;
visibility: hidden;
max-height: 0;
}
.collapsible-wrapper.collapsed:after
{
height: 0;
transition: height 0.3s linear;
max-height: 50px;
}
/* END of the collapsible implementation; the stuff below
is just styling for this demo */
#container {
display: flex;
align-items: flex-start;
max-width: 1000px;
margin: 0 auto;
}
.menu {
border: 1px solid #ccc;
box-shadow: 0 1px 3px rgba(0,0,0,0.5);
margin: 20px;
}
.menu-item {
display: block;
background: linear-gradient(to bottom, #fff 0%,#eee 100%);
margin: 0;
padding: 1em;
line-height: 1.3;
}
.collapsible .menu-item {
border-left: 2px solid #888;
border-right: 2px solid #888;
background: linear-gradient(to bottom, #eee 0%,#ddd 100%);
}
.menu-item.toggler {
background: linear-gradient(to bottom, #aaa 0%,#888 100%);
color: white;
cursor: pointer;
}
.menu-item.toggler:before {
content: '';
display: block;
border-left: 8px solid white;
border-top: 8px solid transparent;
border-bottom: 8px solid transparent;
width: 0;
height: 0;
float: right;
transition: transform 0.3s ease-out;
}
.menu-item.toggler.toggled:before {
transform: rotate(90deg);
}
body { font-family: sans-serif; font-size: 14px; }
*, *:after {
box-sizing: border-box;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="container">
<div class="menu">
<div class="menu-item">Something involving a holodeck</div>
<div class="menu-item">Send an away team</div>
<div class="menu-item toggler">Advanced solutions</div>
<div class="collapsible-wrapper collapsed">
<div class="collapsible">
<div class="menu-item">Separate saucer</div>
<div class="menu-item">Send an away team that includes the captain (despite Riker's protest)</div>
<div class="menu-item">Ask Worf</div>
<div class="menu-item">Something involving Wesley, the 19th century, and a holodeck</div>
<div class="menu-item">Ask Q for help</div>
</div>
</div>
<div class="menu-item">Sweet-talk the alien aggressor</div>
<div class="menu-item">Re-route power from auxiliary systems</div>
</div>
<div class="menu">
<div class="menu-item">Something involving a holodeck</div>
<div class="menu-item">Send an away team</div>
<div class="menu-item toggler">Advanced solutions</div>
<div class="collapsible-wrapper collapsed">
<div class="collapsible">
<div class="menu-item">Separate saucer</div>
<div class="menu-item">Send an away team that includes the captain (despite Riker's protest)</div>
</div>
</div>
<div class="menu-item">Sweet-talk the alien aggressor</div>
<div class="menu-item">Re-route power from auxiliary systems</div>
</div>
<div class="menu">
<div class="menu-item">Something involving a holodeck</div>
<div class="menu-item">Send an away team</div>
<div class="menu-item toggler">Advanced solutions</div>
<div class="collapsible-wrapper collapsed">
<div class="collapsible">
<div class="menu-item">Separate saucer</div>
<div class="menu-item">Send an away team that includes the captain (despite Riker's protest)</div>
<div class="menu-item">Ask Worf</div>
<div class="menu-item">Something involving Wesley, the 19th century, and a holodeck</div>
<div class="menu-item">Ask Q for help</div>
<div class="menu-item">Separate saucer</div>
<div class="menu-item">Send an away team that includes the captain (despite Riker's protest)</div>
<div class="menu-item">Ask Worf</div>
<div class="menu-item">Something involving Wesley, the 19th century, and a holodeck</div>
<div class="menu-item">Ask Q for help</div>
</div>
</div>
<div class="menu-item">Sweet-talk the alien aggressor</div>
<div class="menu-item">Re-route power from auxiliary systems</div>
</div>
</div>
How does it work?
它是如何工作的?
There are in fact twotransitions involved in making this happen. One of them transitions the margin-bottom
from 0px (in the expanded state) to -2000px
in the collapsed state (similar to this answer). The 2000 here is the first magic number, it's based on the assumption that your box won't be higher than this (2000 pixels seems like a reasonable choice).
事实上,要实现这一点涉及两个转变。其中之一将margin-bottom
0px(处于展开状态)转换-2000px
为处于折叠状态(类似于此答案)。这里的 2000 是第一个幻数,它基于假设您的框不会高于此值(2000 像素似乎是一个合理的选择)。
Using the margin-bottom
transition alone by itself has two issues:
margin-bottom
单独使用转换本身有两个问题:
- If you actually have a box that's higher than 2000 pixels, then a
margin-bottom: -2000px
won't hide everything -- there'll be visible stuff even in the collapsed case. This is a minor fix that we'll do later. - If the actual box is, say, 1000 pixels high, and your transition is 300ms long, then the visibletransition is already over after about 150ms (or, in the opposite direction, starts 150ms late).
- 如果您确实有一个高于 2000 像素的框,那么 a
margin-bottom: -2000px
不会隐藏所有内容——即使在折叠的情况下也会有可见的东西。这是我们稍后会做的一个小修复。 - 如果实际的框高 1000 像素,而您的过渡长 300 毫秒,那么可见过渡在大约 150毫秒后就已经结束(或者,在相反的方向,延迟150 毫秒开始)。
Fixing this second issue is where the second transition comes in, and this transition conceptually targets the wrapper's minimumheight ("conceptually" because we're not actually using the min-height
property for this; more on that later).
解决第二个问题是第二个过渡出现的地方,这个过渡在概念上以包装器的最小高度为目标(“概念上”,因为我们实际上并没有min-height
为此使用该属性;稍后会详细介绍)。
Here's an animation that shows how combining the bottom margin transition with the minimum height transition, both of equal duration, gives us a combined transition from full height to zero height that has the same duration.
这是一个动画,展示了如何将底部边距过渡与最小高度过渡(两者的持续时间相同)相结合,为我们提供从全高到零高度且持续时间相同的组合过渡。
The left bar shows how the negative bottom margin pushes the bottom upwards, reducing the visible height. The middle bar shows how the minimum height ensures that in the collapsing case, the transition doesn't end early, and in the expanding case, the transition doesn't start late. The right bar shows how the combination of the two causes the box to transition from full height to zero height in the correct amount of time.
左边的栏显示了负底边距如何向上推动底部,降低可见高度。中间条显示最小高度如何确保在折叠情况下过渡不会提前结束,而在展开情况下过渡不会延迟开始。右栏显示了两者的组合如何使盒子在正确的时间内从全高过渡到零高度。
For my demo I've settled on 50px as the upper minimum height value. This is the second magic number, and it should be lower than the box' height would ever be. 50px seems reasonable as well; it seems unlikely that you'd very often want to make an element collapsible that isn't even 50 pixels high in the first place.
对于我的演示,我已将 50px 作为上限最小高度值。这是第二个幻数,它应该低于盒子的高度。50px 似乎也合理;您似乎不太可能经常想让一个元素一开始甚至不是 50 像素高的可折叠元素。
As you can see in the animation, the resulting transition is continuous, but it is not differentiable -- at the moment when the minimum height is equal to the full height adjusted by the bottom margin, there is a sudden change in speed. This is very noticeable in the animation because it uses a linear timing function for both transitions, and because the whole transition is very slow. In the actual case (my demo at the top), the transition only takes 300ms, and the bottom margin transition is not linear. I've played around with a lot of different timing functions for both transitions, and the ones I ended up with felt like they worked best for the widest variety of cases.
正如你在动画中看到的,产生的过渡是连续的,但它是不可微的——在最小高度等于底部边距调整的全高的那一刻,速度发生了突然的变化。这在动画中非常明显,因为它对两个过渡都使用了线性计时函数,而且整个过渡非常缓慢。在实际情况下(我在顶部的演示),过渡只需要 300 毫秒,底部边距过渡不是线性的。我已经为这两种转换使用了许多不同的计时功能,我最终觉得它们最适合最广泛的情况。
Two problems remain to fix:
还有两个问题需要解决:
- the point from above, where boxes of more than 2000 pixels height aren't completely hidden in the collapsed state,
- and the reverse problem, where in the non-hidden case, boxes of less than 50 pixels height are too high even when the transition isn't running, because the minimum height keeps them at 50 pixels.
- 上面的点,超过 2000 像素高度的盒子在折叠状态下没有完全隐藏,
- 和相反的问题,在非隐藏的情况下,即使没有运行过渡,高度小于 50 像素的框也太高了,因为最小高度使它们保持在 50 像素。
We solve the first problem by giving the container element a max-height: 0
in the collapsed case, with a 0s 0.3s
transition. This means that it's not really a transition, but the max-height
is applied with a delay; it only applies once the transition is over. For this to work correctly, we also need to pick a numerical max-height
for the opposite, non-collapsed, state. But unlike in the 2000px case, where picking too large of a number affects the quality of the transition, in this case, it really doesn't matter. So we can just pick a number that is so high that we knowthat no height will ever come close to this. I picked a million pixels. If you feel you may need to support content of a height of more than a million pixels, then 1) I'm sorry, and 2) just add a couple of zeros.
我们通过max-height: 0
在折叠情况下为容器元素 a提供一个0s 0.3s
过渡来解决第一个问题。这意味着它不是真正的过渡,而是max-height
延迟应用;它仅在过渡结束后适用。为了使其正常工作,我们还需要max-height
为相反的、未折叠的状态选择一个数字。但与 2000px 的情况不同,选择太大的数字会影响过渡的质量,在这种情况下,这真的无关紧要。所以我们可以选择一个非常高的数字,我们知道没有任何高度会接近这个数字。我选择了一百万像素。如果您觉得您可能需要支持高度超过一百万像素的内容,那么 1) 对不起,以及 2) 只需添加几个零。
The second problem is the reason why we're not actually using min-height
for the minimum height transition. Instead, there is an ::after
pseudo-element in the container with a height
that transitions from 50px to zero. This has the same effect as a min-height
: It won't let the container shrink below whatever height the pseudo-element currently has. But because we're using height
, not min-height
, we can now use max-height
(once again applied with a delay) to set the pseudo-element's actual height to zero once the transition is over, ensuring that at least outside the transition, even small elements have the correct height. Because min-height
is strongerthan max-height
, this wouldn't work if we used the container's min-height
instead of the pseudo-element's height
. Just like the max-height
in the previous paragraph, this max-height
also needs a value for the opposite end of the transition. But in this case we can just pick the 50px.
第二个问题是我们实际上没有min-height
用于最小高度过渡的原因。相反,::after
容器中有一个height
从 50px 过渡到 0的伪元素。这与 a 具有相同的效果min-height
:它不会让容器缩小到伪元素当前具有的任何高度以下。但是因为我们使用的是height
, not min-height
,所以我们现在可以使用max-height
(再次延迟应用)在过渡结束后将伪元素的实际高度设置为零,确保至少在过渡之外,即使是小元素也具有正确的高度。因为min-height
是强比max-height
,如果我们使用这是行不通的容器的min-height
,而不是伪元素height
. 就像max-height
上一段中的 ,这max-height
也需要转换的另一端的值。但在这种情况下,我们可以选择 50px。
Tested in Chrome (Win, Mac, Android, iOS), Firefox (Win, Mac, Android), Edge, IE11 (except for a flexbox layout issue with my demo that I didn't bother debugging), and Safari (Mac, iOS). Speaking of flexbox, it should be possible to make this work without using any flexbox; in fact I think you could make almost everything work in IE7 – except for the fact that you won't have CSS transitions, making it a rather pointless exercise.
在 Chrome(Win、Mac、Android、iOS)、Firefox(Win、Mac、Android)、Edge、IE11(除了我没有调试的演示中的 flexbox 布局问题)和 Safari(Mac、iOS)中测试)。说到 flexbox,应该可以在不使用任何 flexbox 的情况下完成这项工作;事实上,我认为你可以在 IE7 中使几乎所有东西都可以工作——除了你不会有 CSS 转换这一事实之外,这使它成为一个毫无意义的练习。
回答by jhurshman
You can, with a little bit of non-semantic jiggery-pokery. My usual approach is to animate the height of an outer DIV which has a single child which is a style-less DIV used only for measuring the content height.
你可以,用一点非语义jiggery-pokery。我通常的方法是为外部 DIV 的高度设置动画,该 DIV 有一个孩子,这是一个无样式的 DIV,仅用于测量内容高度。
function growDiv() {
var growDiv = document.getElementById('grow');
if (growDiv.clientHeight) {
growDiv.style.height = 0;
} else {
var wrapper = document.querySelector('.measuringWrapper');
growDiv.style.height = wrapper.clientHeight + "px";
}
}
#grow {
-moz-transition: height .5s;
-ms-transition: height .5s;
-o-transition: height .5s;
-webkit-transition: height .5s;
transition: height .5s;
height: 0;
overflow: hidden;
outline: 1px solid red;
}
<input type="button" onclick="growDiv()" value="grow">
<div id='grow'>
<div class='measuringWrapper'>
<div>
The contents of my div.
</div>
<div>
The contents of my div.
</div>
<div>
The contents of my div.
</div>
<div>
The contents of my div.
</div>
<div>
The contents of my div.
</div>
<div>
The contents of my div.
</div>
</div>
</div>
One would like to just be able to dispense with the .measuringWrapper
and just set the DIV's height to auto and have that animate, but that doesn't seem to work (the height gets set, but no animation occurs).
人们希望能够免除.measuringWrapper
并将 DIV 的高度设置为 auto 并具有该动画,但这似乎不起作用(设置了高度,但没有动画发生)。
function growDiv() {
var growDiv = document.getElementById('grow');
if (growDiv.clientHeight) {
growDiv.style.height = 0;
} else {
growDiv.style.height = 'auto';
}
}
#grow {
-moz-transition: height .5s;
-ms-transition: height .5s;
-o-transition: height .5s;
-webkit-transition: height .5s;
transition: height .5s;
height: 0;
overflow: hidden;
outline: 1px solid red;
}
<input type="button" onclick="growDiv()" value="grow">
<div id='grow'>
<div>
The contents of my div.
</div>
<div>
The contents of my div.
</div>
<div>
The contents of my div.
</div>
<div>
The contents of my div.
</div>
<div>
The contents of my div.
</div>
<div>
The contents of my div.
</div>
</div>
My interpretation is that an explicit height is needed for the animation to run. You can't get an animation on height when either height (the start or end height) is auto
.
我的解释是动画运行需要明确的高度。当高度(开始或结束高度)为auto
.
回答by Catharsis
A visual workaround to animating height using CSS3 transitions is to animate the padding instead.
使用 CSS3 过渡动画高度的视觉解决方法是动画填充。
You don't quite get the full wipe effect, but playing around with the transition-duration and padding values should get you close enough. If you don't want to explicitly set height/max-height, this should be what you're looking for.
您并没有完全获得完整的擦除效果,但是使用过渡持续时间和填充值应该会让您足够接近。如果您不想明确设置高度/最大高度,这应该是您要找的。
div {
height: 0;
overflow: hidden;
padding: 0 18px;
-webkit-transition: all .5s ease;
-moz-transition: all .5s ease;
transition: all .5s ease;
}
div.animated {
height: auto;
padding: 24px 18px;
}
http://jsfiddle.net/catharsis/n5XfG/17/(riffed off stephband's above jsFiddle)
http://jsfiddle.net/catharsis/n5XfG/17/(从 jsFiddle 上面的 stephband 中删除)
回答by Adam
My workaround is to transition max-height to the exact content height for a nice smooth animation, then use a transitionEnd callback to set max-height to 9999px so the content can resize freely.
我的解决方法是将 max-height 转换为精确的内容高度以获得漂亮的平滑动画,然后使用 transitionEnd 回调将 max-height 设置为 9999px,以便内容可以自由调整大小。
var content = $('#content');
content.inner = $('#content .inner'); // inner div needed to get size of content when closed
// css transition callback
content.on('transitionEnd webkitTransitionEnd transitionend oTransitionEnd msTransitionEnd', function(e){
if(content.hasClass('open')){
content.css('max-height', 9999); // try setting this to 'none'... I dare you!
}
});
$('#toggle').on('click', function(e){
content.toggleClass('open closed');
content.contentHeight = content.outerHeight();
if(content.hasClass('closed')){
// disable transitions & set max-height to content height
content.removeClass('transitions').css('max-height', content.contentHeight);
setTimeout(function(){
// enable & start transition
content.addClass('transitions').css({
'max-height': 0,
'opacity': 0
});
}, 10); // 10ms timeout is the secret ingredient for disabling/enabling transitions
// chrome only needs 1ms but FF needs ~10ms or it chokes on the first animation for some reason
}else if(content.hasClass('open')){
content.contentHeight += content.inner.outerHeight(); // if closed, add inner height to content height
content.css({
'max-height': content.contentHeight,
'opacity': 1
});
}
});
.transitions {
transition: all 0.5s ease-in-out;
-webkit-transition: all 0.5s ease-in-out;
-moz-transition: all 0.5s ease-in-out;
}
body {
font-family:Arial;
line-height: 3ex;
}
code {
display: inline-block;
background: #fafafa;
padding: 0 1ex;
}
#toggle {
display:block;
padding:10px;
margin:10px auto;
text-align:center;
width:30ex;
}
#content {
overflow:hidden;
margin:10px;
border:1px solid #666;
background:#efefef;
opacity:1;
}
#content .inner {
padding:10px;
overflow:auto;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<div id="content" class="open">
<div class="inner">
<h3>Smooth CSS Transitions Between <code>height: 0</code> and <code>height: auto</code></h3>
<p>A clever workaround is to use <code>max-height</code> instead of <code>height</code>, and set it to something bigger than your content. Problem is the browser uses this value to calculate transition duration. So if you set it to <code>max-height: 1000px</code> but the content is only 100px high, the animation will be 10x too fast.</p>
<p>Another option is to measure the content height with JS and transition to that fixed value, but then you have to keep track of the content and manually resize it if it changes.</p>
<p>This solution is a hybrid of the two - transition to the measured content height, then set it to <code>max-height: 9999px</code> after the transition for fluid content sizing.</p>
</div>
</div>
<br />
<button id="toggle">Challenge Accepted!</button>
回答by Oleg Vaskevich
The accepted answer works for most cases, but it doesn't work well when your div
can vary greatly in height — the animation speed is not dependent on the actual height of the content, and it can look choppy.
已接受的答案适用于大多数情况,但当您的div
高度变化很大时效果不佳- 动画速度不依赖于内容的实际高度,并且看起来可能不稳定。
You can still perform the actual animation with CSS, but you need to use JavaScript to compute the height of the items, instead of trying to use auto
. No jQuery is required, although you may have to modify this a bit if you want compatibility (works in the latest version of Chrome :)).
您仍然可以使用 CSS 执行实际动画,但您需要使用 JavaScript 来计算项目的高度,而不是尝试使用auto
. 不需要 jQuery,但如果您想要兼容性,您可能需要对其进行一些修改(适用于最新版本的 Chrome :))。
window.toggleExpand = function(element) {
if (!element.style.height || element.style.height == '0px') {
element.style.height = Array.prototype.reduce.call(element.childNodes, function(p, c) {return p + (c.offsetHeight || 0);}, 0) + 'px';
} else {
element.style.height = '0px';
}
}
#menu #list {
height: 0px;
transition: height 0.3s ease;
background: #d5d5d5;
overflow: hidden;
}
<div id="menu">
<input value="Toggle list" type="button" onclick="toggleExpand(document.getElementById('list'));">
<ul id="list">
<!-- Works well with dynamically-sized content. -->
<li>item</li>
<li><div style="height: 100px; width: 100px; background: red;"></div></li>
<li>item</li>
<li>item</li>
<li>item</li>
</ul>
</div>
回答by amn
There was little mention of the Element.scrollHeight
property which can be useful here and still may be used with a pure CSS transition. The property always contains the "full" height of an element, regardless of whether and how its content overflows as a result of collapsed height (e.g. height: 0
).
很少提到这个Element.scrollHeight
属性在这里很有用,并且仍然可以与纯 CSS 过渡一起使用。该属性始终包含元素的“完整”高度,无论其内容是否以及如何由于折叠高度(例如height: 0
)而溢出。
As such, for a height: 0
(effectively fully collapsed) element, its "normal" or "full" height is still readily available through its scrollHeight
value (invariably a pixellength).
因此,对于height: 0
(有效完全折叠的)元素,其“正常”或“完整”高度仍然可以通过其scrollHeight
值(总是像素长度)轻松获得。
For such an element, assuming it already has the transition set up like e.g. (using ul
as per original question):
对于这样的元素,假设它已经设置了过渡,例如(ul
根据原始问题使用):
ul {
height: 0;
transition: height 1s; /* An example transition. */
}
We can trigger desired animated "expansion" of height, using CSS only, with something like the following (here assuming ul
variable refers to the list):
我们可以仅使用 CSS 触发所需的高度动画“扩展”,如下所示(这里假设ul
变量指的是列表):
ul.style.height = ul.scrollHeight + "px";
That's it. If you need to collapse the list, either of the two following statements will do:
就是这样。如果您需要折叠列表,以下两个语句中的任何一个都可以:
ul.style.height = "0";
ul.style.removeProperty("height");
My particular use case revolved around animating lists of unknown and often considerable lengths, so I was not comfortable settling on an arbitrary "large enough" height
or max-height
specification and risking cut-off content or content that you suddenly need to scroll (if overflow: auto
, for example). Additionally, the easing and timing is broken with max-height
-based solutions, because the used height may reach its maximum value a lot sooner than it would take for max-height
to reach 9999px
. And as screen resolutions grow, pixel lengths like 9999px
leave a bad taste in my mouth. This particular solution solves the problem in an elegant manner, in my opinion.
我的特定用例围绕着未知且通常相当长的动画列表展开,因此我不喜欢随意选择“足够大”height
或max-height
规范,并冒着被截断的内容或您突然需要滚动的内容的风险(overflow: auto
例如,如果) . 此外,宽松和时间与破max-height
基于解决方案,因为所使用的高度可达到最大值很多早于它会采取对max-height
REACH法规9999px
。随着屏幕分辨率的提高,像素长度就像9999px
在我嘴里留下了不好的味道。在我看来,这个特殊的解决方案以一种优雅的方式解决了这个问题。
Finally, here is hoping that future revisions of CSS address authors' need to do these kind of things even more elegantly -- revisit the notion of "computed" vs "used" and "resolved" values, and consider whether transitions should apply to computed values, including transitions with width
and height
(which currently get a bit of a special treatment).
最后,这里希望 CSS 地址作者的未来修订需要更优雅地做这些事情——重新审视“计算”与“使用”和“已解决”值的概念,并考虑转换是否应该应用于计算值,包括使用width
和的转换height
(目前得到了一些特殊处理)。