css - transition触发 - transition阮一峰




我怎样才能过渡高度:0; 高度:自动; 用CSS? (20)

Flexbox解决方案

优点:

  • 简单
  • 没有JS
  • 平稳过渡

缺点:

  • 元素需要放在固定高度的flex容器中

它的工作方式是始终使用flex-basis:对包含内容的元素执行auto,然后转换flex-grow和flex-shrink。

编辑:改进的JS小提琴灵感来自Xbox One界面。

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  transition: 0.25s;
  font-family: monospace;
}

body {
  margin: 10px 0 0 10px;
}

.box {
  width: 150px;
  height: 150px;
  margin: 0 2px 10px 0;
  background: #2d333b;
  border: solid 10px #20262e;
  overflow: hidden;
  display: inline-flex;
  flex-direction: column;
}

.space {
  flex-basis: 100%;
  flex-grow: 1;
  flex-shrink: 0;    
}

p {
  flex-basis: auto;
  flex-grow: 0;
  flex-shrink: 1;
  background: #20262e;
  padding: 10px;
  width: 100%;
  text-align: left;
  color: white;
}

.box:hover .space {
  flex-grow: 0;
  flex-shrink: 1;
}
  
.box:hover p {
  flex-grow: 1;
  flex-shrink: 0;    
}
<div class="box">
  <div class="space"></div>
  <p>
    Super Metroid Prime Fusion
  </p>
</div>
<div class="box">
  <div class="space"></div>
  <p>
    Resident Evil 2 Remake
  </p>
</div>
<div class="box">
  <div class="space"></div>
  <p>
    Yolo The Game
  </p>
</div>
<div class="box">
  <div class="space"></div>
  <p>
    Final Fantasy 7 Remake + All Additional DLC + Golden Tophat
  </p>
</div>
<div class="box">
  <div class="space"></div>
  <p>
    DerpVille
  </p>
</div>

JS小提琴

我试图使用CSS过渡使<ul>向下滑动。

<ul>height: 0;开始height: 0; 。 悬停时,高度设置为height:auto; 。 然而,这导致它只是出现而不是过渡,

如果我从height: 40px;height: 40px; height: auto; ,然后它会滑到height: 0; ,然后突然跳到正确的高度。

如果不使用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>


我想我想出了一个非常可靠的解决方案

好!我知道这个问题与互联网一样古老,但我认为我有一个解决方案,我变成了一个名为突变转换插件style=""每当DOM发生变化时,我的解决方案都会为跟踪元素设置属性。最终结果是你可以使用好的ole CSS进行转换,而不是使用hacky修复程序或特殊的javascript。您唯一需要做的就是使用相关元素设置要跟踪的内容data-mutant-attributes="X"

<div data-mutant-attributes="height">                                                                      
        This is an example with mutant-transition                                                                                                          
    </div>

而已!此解决方案使用MutationObserver来跟踪DOM中的更改。因此,您不需要设置任何内容或使用javascript手动设置动画。自动跟踪更改。但是,因为它使用MutationObserver,所以这只会在IE11 +中进行转换。

小提琴!


使用CSS3过渡动画高度的可视化解决方法是为填充设置动画。

你没有完全获得完全擦除效果,但是使用transition-duration和padding值可以让你足够接近。 如果您不想明确设置height / max-height,那么这应该是您正在寻找的。

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/ (在jsFiddle之上翻过stephband)


在变换中使用max-height而不是height 。 并将max-height上的值设置为比您的盒子更大的值。

请参阅Chris Jordan在另一个answer提供的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>


当其中一个高度为auto ,您无法在高度上设置动画,您必须设置两个明确的高度。


您可以从height:0过渡到height:auto,同时提供最小高度和最大高度。

div.stretchy{
    transition: 1s linear;
}

div.stretchy.hidden{
    height: 0;
}

div.stretchy.visible{
    height: auto;
    min-height:40px;
    max-height:400px;
}

我一直使用的解决方案是先淡出,然后缩小font-sizepaddingmargin值。 它与擦拭看起来不一样,但它没有静态heightmax-height

工作范例:

/* 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>


我意识到这个帖子已经老了,但它在某些谷歌搜索中排名很高,所以我认为它值得更新。

您还可以获取/设置元素自己的高度:

var load_height = document.getElementById('target_box').clientHeight;
document.getElementById('target_box').style.height = load_height + 'px';

您应该在内联脚本标记中的target_box的结束标记之后立即转储此Javascript。


我的解决方法是将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>


我知道这是这个问题的三十分之一的答案,但我认为这是值得的,所以这里有。 这是一个仅支持CSS的解决方案,具有以下属性:

  • 一开始没有延迟,过渡不会提前停止。 在两个方向(展开和折叠)中,如果在CSS中指定300毫秒的转换持续时间,则转换需要300毫秒(周期)。
  • 它正在转换实际高度(与transform: scaleY(0) ),因此如果在可折叠元素之后有内容,它会做正确的事情。
  • 虽然(和其他解决方案一样)有魔术数字(比如“选择一个比你的盒子更高的长度”),如果你的假设最终错了,那就不是致命的。 在这种情况下,转换可能看起来并不惊人,但在转换之前和之后,这不是问题:在扩展( height: auto )状态下,整个内容始终具有正确的高度(例如,如果您选择max-height结果太高了。 在崩溃的状态下,高度应该是零。

演示

这是一个包含三个可折叠元素的演示,所有元素都具有不同的高度,所有元素都使用相同的CSS。 单击“运行代码段”后,您可能需要单击“完整页面”。 请注意,JavaScript仅切换collapsed CSS类,不涉及测量。 (你可以使用复选框或:target没有任何JavaScript的精确演示)。 另请注意,负责转换的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>

它是如何工作的?

实际上有两个转变涉及实现这一点。 其中一个将margin-bottom从0px(处于展开状态) -2000px为折叠状态下的-2000px (类似于此答案 )。 这里的2000是第一个神奇的数字,它基于你的盒子不会高于此的假设(2000像素似乎是一个合理的选择)。

单独使用margin-bottom过渡本身有两个问题:

  • 如果你实际上有一个高于2000像素的盒子,那么margin-bottom: -2000px将不会隐藏所有内容 - 即使在折叠的情况下也会有可见的东西。 这是一个小修复,我们稍后会做。
  • 如果实际的盒子是1000像素高,并且你的过渡是300ms长,那么可见的过渡在大约150ms之后已经结束(或者,相反的方向,开始晚150ms)。

修复第二个问题是第二个转换的位置,这个转换在概念上针对包装器的最小高度(“概念上”,因为我们实际上并没有使用min-height属性;稍后将详细介绍)。

这是一个动画,显示了如何将底部边缘过渡与最小高度过渡相结合,两者具有相同的持续时间,为我们提供了从全高度到零高度的组合过渡,具有相同的持续时间。

左侧栏显示负底部边缘如何向下推动底部,降低可见高度。 中间栏显示最小高度如何确保在折叠情况下,过渡不会提前结束,并且在扩展的情况下,过渡不会延迟。 右侧栏显示两者的组合如何使盒子在正确的时间内从全高度过渡到零高度。

对于我的演示,我已经确定50px作为最小高度值的上限。 这是第二个神奇的数字,它应该低于盒子的高度。 50px似乎也是合理的; 你似乎不太可能经常想要制作一个折叠的元素,它首先不是50像素高。

正如您在动画中看到的那样,所产生的过渡是连续的,但它是不可微分的 - 当最小高度等于由下边距调整的全高时,速度会突然改变。 这在动画中非常明显,因为它对两个过渡使用线性定时功能,并且因为整个过渡非常慢。 在实际情况下(我的演示在顶部),转换只需要300毫秒,底部边距转换不是线性的。 我已经为这两种转换玩了很多不同的计时功能,而我最终感觉它们最适合各种各样的情况。

还有两个问题需要解决:

  1. 从上面开始,在折叠状态下,超过2000像素高度的盒子没有被完全隐藏,
  2. 和相反的问题,在非隐藏的情况下,即使转换未运行,高度小于50像素的框也太高,因为最小高度使它们保持在50像素。

我们通过在折叠的情况下给容器元素一个max-height: 0解决第一个问题,转换为0s 0.3s 。 这意味着它不是真正的过渡,但max-height应用了延迟; 它只适用于转换结束后。 为了使其正常工作,我们还需要为相反的非折叠状态选择数字max-height 。 但与2000px的情况不同,选择太大的数字会影响转换的质量,在这种情况下,它确实无关紧要。 所以我们可以选择一个如此之高的数字,以便我们知道没有任何高度可以接近这一点。 我选了一百万像素。 如果您觉得可能需要支持高度超过一百万像素的内容,那么1)我很抱歉,2)只需添加几个零。

第二个问题是我们实际上没有使用min-height进行最小高度转换的原因。 相反,容器中有一个::after伪元素,其height从50px过渡到零。 这与min-height具有相同的效果:它不会让容器收缩到伪元素当前所具有的任何高度以下。 但是因为我们使用的是height ,而不是min-height ,我们现在可以使用max-height (再次应用延迟)将转换结束时伪元素的实际高度设置为零,确保至少在过渡,即使是小元素也有正确的高度。 因为min-heightmax-height min-height stronger ,所以如果我们使用容器的min-height而不是伪元素的height ,这将不起作用。 就像前一段中的max-height一样,这个max-height也需要一个转换另一端的值。 但在这种情况下,我们可以选择50px。

测试过Chrome(Win,Mac,Android,iOS),Firefox(Win,Mac,Android),Edge,IE11(除了我的演示中的flexbox布局问题,我没有打扰调试)和Safari(Mac,iOS) )。 说到flexbox,应该可以在不使用任何flexbox的情况下完成这项工作; 事实上,我认为你几乎可以使所有东西都在IE7中工作 - 除了你不会有CSS过渡这一事实,这使得它成为一个毫无意义的练习。


杰克对最大高度动画的答案很棒,但我发现设置一个大的最大高度令人烦恼的延迟。

可以将可折叠内容移动到内部div中,并通过获取内部div的高度来计算最大高度(通过JQuery,它将是outerHeight())。

$('button').bind('click', function(e) { 
  e.preventDefault();
  w = $('#outer');
  if (w.hasClass('collapsed')) {
    w.css({ "max-height": $('#inner').outerHeight() + 'px' });
  } else {
    w.css({ "max-height": "0px" });
  }
  w.toggleClass('collapsed');
});

这是一个jsfiddle链接:http://jsfiddle.net/pbatey/duZpThttp://jsfiddle.net/pbatey/duZpT

这是一个jsfiddle,需要绝对最少的代码:http://jsfiddle.net/8ncjjxh8/http://jsfiddle.net/8ncjjxh8/


没有硬编码值。

没有JavaScript。

没有近似值。

诀窍是使用隐藏和重复div,让浏览器了解100%的含义。

只要您能够复制要设置动画的元素的DOM,此方法就适用。

.outer {
  border: dashed red 1px;
  position: relative;
}

.dummy {
  visibility: hidden;
}

.real {
  position: absolute;
  background: yellow;
  height: 0;
  transition: height 0.5s;
  overflow: hidden;
}

.outer:hover>.real {
  height: 100%;
}
Hover over the box below:
<div class="outer">
  <!-- The actual element that you'd like to animate -->
  <div class="real">
unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable
content unpredictable content unpredictable content unpredictable content
  </div>
  <!-- An exact copy of the element you'd like to animate. -->
  <div class="dummy" aria-hidden="true">
unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable
content unpredictable content unpredictable content unpredictable content
  </div>
</div>


max-height对每个州使用不同的转换缓和和延迟。

HTML:

<a href="#" id="trigger">Hover</a>
<ul id="toggled">
    <li>One</li>
    <li>Two</li>
    <li>Three</li>
<ul>

CSS:

#toggled{
    max-height: 0px;
    transition: max-height .8s cubic-bezier(0, 1, 0, 1) -.1s;
}

#trigger:hover + #toggled{
    max-height: 9999px;
    transition-timing-function: cubic-bezier(0.5, 0, 1, 0); 
    transition-delay: 0s;
}

参见示例:http://jsfiddle.net/0hnjehjc/1/http://jsfiddle.net/0hnjehjc/1/


几乎没有提到在Element.scrollHeight这里有用的属性,仍然可以用于纯CSS过渡。该属性始终包含元素的“完整”高度,无论其内容是否以及如何因折叠高度而溢出(例如height: 0)。

因此,对于height: 0(有效完全折叠的)元素,其“正常”或“完整”高度仍然可通过其scrollHeight值(总是像素长度)容易地获得。

对于这样的元素,假设它已经具有类似的转换设置(使用ul原始问题):

ul {
    height: 0;
    transition: height 1s; /* An example transition. */
}

我们可以使用CSS来触发所需的动画“高度扩展”,使用类似下面的内容(这里假设ul变量引用列表):

ul.style.height = ul.scrollHeight + "px";

而已。 如果您需要折叠列表,则以下两个语句中的任何一个都会执行以下操作:

ul.style.height = "0";
ul.style.removeProperty("height");

我的特定用例围绕着未知且通常相当长的动画列表,因此我不习惯定制任意“足够大” heightmax-height规范,并冒着截断内容或内容突然需要滚动的内容(overflow: auto例如,如果) 。此外,宽松和时间与破max-height基于解决方案,因为所使用的高度可达到最大值很多早于它会采取max-height到达9999px。随着屏幕分辨率的增长,像素长度就像9999px在我嘴里留下了不好的味道。在我看来,这个特殊的解决方案以优雅的方式解决了这个问题。

最后,这里希望CSS的未来版本能够更好地解决作者需要更好地完成这些事情 - 重新审视“计算”与“已使用”和“已解决”值的概念,并考虑转换是否应适用于计算值,包括与width和的转换height(目前得到一些特殊处理)。


如果提供的硬编码最大高度值不比实际高度大很多(因为否则存在不希望的延迟和定时问题),Jake的最大高度解决方案效果很好。另一方面,如果硬编码值意外地不大于实际高度,则元素将不会完全打开。

以下仅CSS解决方案还需要硬编码大小,该大小应该大于大多数发生的实际大小。但是,如果实际大小在某些情况下大于硬编码大小,则此解决方案也可以使用。在那种情况下,转换可能会稍微跳转,但它永远不会留下部分可见的元素。因此,此解决方案也可用于未知内容,例如来自数据库,您只知道内容通常不大于x像素,但也有例外。

想法是对margin-bottom(或稍微不同的动画的margin-top)使用负值,并将content元素放入具有overflow:hidden的中间元素。内容元素的负边距因此降低了中间元素的高度。

以下代码使用从-150px到0px的margin-bottom转换。只要内容元素不高于150px,这一点就可以正常工作。此外,它使用中间元素的最大高度转换,从0px到100%。如果内容元素高于150px,这最终会隐藏中间元素。对于最大高度,过渡仅用于在关闭时将其应用延迟一秒,而不是用于平滑的视觉效果(因此它可以从0px到100%运行)。

CSS:

.content {
  transition: margin-bottom 1s ease-in;
  margin-bottom: -150px;
}
.outer:hover .middle .content {
  transition: margin-bottom 1s ease-out;
  margin-bottom: 0px
}
.middle {
  overflow: hidden;
  transition: max-height .1s ease 1s;
  max-height: 0px
}
.outer:hover .middle {
  transition: max-height .1s ease 0s;
  max-height: 100%
}

HTML:

<div class="outer">
  <div class="middle">
    <div class="content">
      Sample Text
      <br> Sample Text
      <br> Sample Text
      <div style="height:150px">Sample Test of height 150px</div>
      Sample Text
    </div>
  </div>
  Hover Here
</div>

margin bottom的值应为负数,并尽可能接近content元素的实际高度。如果它(绝对值)更大,则存在类似于最大高度解决方案的延迟和定时问题,但是只要硬编码尺寸不比实际尺寸大得多,则可以限制。如果margin-bottom的绝对值小于实际高度,则tansition会跳跃一点。在转换之后的任何情况下,内容元素要么完全显示,要么完全删除。

有关详细信息,请参阅我的博客文章http://www.taccgl.org/blog/css_transition_display.html#combined_height


我最近一直在转变max-heightli元素,而不是包装ul

其理由是,对于小的延迟max-heights远没有那么明显(如果有的话)相比大max-heights了,我也可以把我的max-height相对值font-sizeli使用,而不是一些任意数量巨大ems或者rems

如果我的字体大小是1rem,我会将我设置max-height为类似3rem(以容纳包装文本)。你可以在这里看到一个例子:

http://codepen.io/mindfullsilence/pen/DtzjE


接受的答案适用于大多数情况,但是当你的div高度变化很大时它不能很好地运行 - 动画速度不依赖于内容的实际高度,并且它看起来不稳定。

您仍然可以使用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>


正如我发布的那样,已有超过30个答案,但我觉得我的答案在杰克已经接受的答案上有所改进。

我不满足于简单地使用max-height和CSS3过渡所引起的问题,因为许多评论者指出,你必须将你的max-height值设置得非常接近实际高度,否则你会得到一个延迟。有关该问题的示例,请参阅此JSFiddle

为了解决这个问题(虽然仍然没有使用JavaScript),我添加了另一个转换transform: translateYCSS值的HTML元素。

这意味着使用max-heighttranslateY使用:max-height允许元素按下它下面的元素,同时translateY给出我们想要的“即时”效果。问题max-height仍然存在,但其影响有所减轻。这意味着您可以为您的max-height价值设置更大的高度,并减少对它的担忧。

总体好处是,在转换回(崩溃)时,用户会translateY立即看到动画,因此max-height花费多长时间并不重要。

解决方案如小提琴

body {
  font-family: sans-serif;
}

.toggle {
  position: relative;
  border: 2px solid #333;
  border-radius: 3px;
  margin: 5px;
  width: 200px;
}

.toggle-header {
  margin: 0;
  padding: 10px;
  background-color: #333;
  color: white;
  text-align: center;
  cursor: pointer;
}

.toggle-height {
  background-color: tomato;
  overflow: hidden;
  transition: max-height .6s ease;
  max-height: 0;
}

.toggle:hover .toggle-height {
  max-height: 1000px;
}

.toggle-transform {
  padding: 5px;
  color: white;
  transition: transform .4s ease;
  transform: translateY(-100%);
}

.toggle:hover .toggle-transform {
  transform: translateY(0);
}
<div class="toggle">
  <div class="toggle-header">
    Toggle!
  </div>
  <div class="toggle-height">
    <div class="toggle-transform">
      <p>Content!</p>
      <p>Content!</p>
      <p>Content!</p>
      <p>Content!</p>
    </div>
  </div>
</div>

<div class="toggle">
  <div class="toggle-header">
    Toggle!
  </div>
  <div class="toggle-height">
    <div class="toggle-transform">
      <p>Content!</p>
      <p>Content!</p>
      <p>Content!</p>
      <p>Content!</p>
    </div>
  </div>
</div>


这不是问题的“解决方案”,而是更多的解决方法。它只能用文本编写,但可以根据需要更改为与其他元素一起使用我敢肯定。

.originalContent {
    font-size:0px;
    transition:font-size .2s ease-in-out;
}
.show { /* class to add to content */
    font-size:14px;
}

这是一个例子:http://codepen.io/overthemike/pen/wzjRKahttp://codepen.io/overthemike/pen/wzjRKa

基本上,您将font-size设置为0并以足够快的速度转换而不是height,max-height或scaleY()等,以获得转换为您想要的高度。目前无法将CSS的实际高度转换为自动,但转换内容是因为字体大小的转换。

  • 注意 - 在codepen中有javascript,但它的唯一目的是在手风琴点击时添加/删除css类。这可以通过隐藏的单选按钮完成,但我没有专注于那个,只是高度转换。

这是一种从任何起始高度(包括0)过渡到自动(完整大小和灵活)的方法,无需基于每个节点的硬件代码或任何初始化的用户代码:https://github.com/csuwildcat/transition-autohttps://github.com/csuwildcat/transition-auto。这基本上是你想要的圣杯,我相信 - > http://codepen.io/csuwldcat/pen/kwsdF。只需将以下JS文件打入您的页面,之后您需要做的就是添加/删除一个布尔属性reveal=""- 来自您要扩展和收缩的节点。

一旦您包含示例代码下方的代码块,您就需要以用户身份完成所有操作:

/*** Nothing out of the ordinary in your styles ***/
<style>
    div {
        height: 0;
        overflow: hidden;
        transition: height 1s;
    }
</style>

/*** Just add and remove one attribute and transition to/from auto! ***/

<div>
    I have tons of content and I am 0px in height you can't see me...
</div>

<div reveal>
     I have tons of content and I am 0px in height you can't see me...
     but now that you added the 'reveal' attribute, 
     I magically transitioned to full height!...
</div>

这是包含在您页面中的代码块,之后,它都是肉汁:

将这个JS文件放在你的页面中 - 这一切都是Just Works™

/ *高度代码:auto; 过渡* /

(function(doc){

/* feature detection for browsers that report different values for scrollHeight when an element's overflow is hidden vs visible (Firefox, IE) */
var test = doc.documentElement.appendChild(doc.createElement('x-reveal-test'));
    test.innerHTML = '-';
    test.style.cssText = 'display: block !important; height: 0px !important; padding: 0px !important; font-size: 0px !important; border-width: 0px !important; line-height: 1px !important; overflow: hidden !important;';
var scroll = test.scrollHeight || 2;
doc.documentElement.removeChild(test);

var loading = true,
    numReg = /^([0-9]*\.?[0-9]*)(.*)/,
    skipFrame = function(fn){
      requestAnimationFrame(function(){
        requestAnimationFrame(fn);
      });
    },
    /* 2 out of 3 uses of this function are purely to work around Chrome's catastrophically busted implementation of auto value CSS transitioning */
    revealFrame = function(el, state, height){
        el.setAttribute('reveal-transition', 'frame');
        el.style.height = height;
        skipFrame(function(){
            el.setAttribute('reveal-transition', state);
            el.style.height = '';
        });
    },
    transitionend = function(e){
      var node = e.target;
      if (node.hasAttribute('reveal')) {
        if (node.getAttribute('reveal-transition') == 'running') revealFrame(node, 'complete', '');
      } 
      else {
        node.removeAttribute('reveal-transition');
        node.style.height = '';
      }
    },
    animationstart = function(e){
      var node = e.target,
          name = e.animationName;   
      if (name == 'reveal' || name == 'unreveal') {

        if (loading) return revealFrame(node, 'complete', 'auto');

        var style = getComputedStyle(node),
            offset = (Number(style.paddingTop.match(numReg)[1])) +
                     (Number(style.paddingBottom.match(numReg)[1])) +
                     (Number(style.borderTopWidth.match(numReg)[1])) +
                     (Number(style.borderBottomWidth.match(numReg)[1]));

        if (name == 'reveal'){
          node.setAttribute('reveal-transition', 'running');
          node.style.height = node.scrollHeight - (offset / scroll) + 'px';
        }
        else {
            if (node.getAttribute('reveal-transition') == 'running') node.style.height = '';
            else revealFrame(node, 'running', node.scrollHeight - offset + 'px');
        }
      }
    };

doc.addEventListener('animationstart', animationstart, false);
doc.addEventListener('MSAnimationStart', animationstart, false);
doc.addEventListener('webkitAnimationStart', animationstart, false);
doc.addEventListener('transitionend', transitionend, false);
doc.addEventListener('MSTransitionEnd', transitionend, false);
doc.addEventListener('webkitTransitionEnd', transitionend, false);

/*
    Batshit readyState/DOMContentLoaded code to dance around Webkit/Chrome animation auto-run weirdness on initial page load.
    If they fixed their code, you could just check for if(doc.readyState != 'complete') in animationstart's if(loading) check
*/
if (document.readyState == 'complete') {
    skipFrame(function(){
        loading = false;
    });
}
else document.addEventListener('DOMContentLoaded', function(e){
    skipFrame(function(){
        loading = false;
    });
}, false);

/* Styles that allow for 'reveal' attribute triggers */
var styles = doc.createElement('style'),
    t = 'transition: none; ',
    au = 'animation: reveal 0.001s; ',
    ar = 'animation: unreveal 0.001s; ',
    clip = ' { from { opacity: 0; } to { opacity: 1; } }',
    r = 'keyframes reveal' + clip,
    u = 'keyframes unreveal' + clip;

styles.textContent = '[reveal] { -ms-'+ au + '-webkit-'+ au +'-moz-'+ au + au +'}' +
    '[reveal-transition="frame"] { -ms-' + t + '-webkit-' + t + '-moz-' + t + t + 'height: auto; }' +
    '[reveal-transition="complete"] { height: auto; }' +
    '[reveal-transition]:not([reveal]) { -webkit-'+ ar +'-moz-'+ ar + ar +'}' +
    '@-ms-' + r + '@-webkit-' + r + '@-moz-' + r + r +
    '@-ms-' + u +'@-webkit-' + u + '@-moz-' + u + u;

doc.querySelector('head').appendChild(styles);

})(document);

/ * DEMO代码* /

    document.addEventListener('click', function(e){
      if (e.target.nodeName == 'BUTTON') {
        var next = e.target.nextElementSibling;
        next.hasAttribute('reveal') ? next.removeAttribute('reveal') : next.setAttribute('reveal', '');
      }
    }, false);




css-transitions