如何转换高度:0; 达到高度:自动;使用CSS?


2131

我正在尝试<ul>使用CSS过渡制作幻灯片。

<ul>在开始关闭height: 0;。悬停时,高度设置为height:auto;。但是,这导致它只是显示而不是过渡,

如果我从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>


我相信该height:auto/max-height解决方案仅在您扩大的区域大于height您要限制的区域时才有效。如果你有一个max-height300px,但组合框下拉菜单中,可以返回50px,则max-height不会帮你,50px是可变的,这取决于元件的数量,就可以到达一个不可能的情况下,我不能修复它,因为height不固定,height:auto是解决方案,但我不能与此使用过渡。
克里斯托弗·托马斯

51
OP尝试使用CSS解决方案,而不是js,否则他们可以使用溢出和动画效果
Toni Leigh

5
@VIDesignz:但是内部div的-100%边距顶部接收包装div 的宽度而不是高度。因此,此解决方案具有与最大高度解决方案相同的问题。此外,当宽度小于内容的高度时,并非所有内容都被-100%的页边距隐藏。因此,这是一个错误的解决方案。
GregTom,2015年

1
请参见github.com/w3c/csswg-drafts/issues/626,以获取有关适当解决方案的规范和实现的讨论
Ben Creasy

Answers:


2743

max-height在过渡中使用,而不是height。并设置一个max-height比您的盒子所能获得的更大的价值。

参见克里斯·乔丹(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>


323
这很棒!除了有一个延迟启动的时候,因为它开始的最大高度最初是非常high..hmm,我认为这是有点恼人
VSYNC

175
+1很棒的解决方案!过渡速度的计算方式是您指定过渡到最大高度值的时间...但是由于高度将小于最大高度,因此过渡到实际高度的速度将比(通常明显)快。指定的时间。
kingjeffrey 2012年

50
请注意,当您必须使用比实际计算值大得多的值时,这可能会导致丑陋的转换结束。我在尝试使div从0高度增长到内容高度时注意到了这一点,该高度由于屏幕尺寸的不同而有很大差异(在我的2560x1440显示器上为2行,而在智能手机上为10行以上)。为此,我最终选择了js。
jpeltoniemi

44
非常丑陋的解决方案,因为它会在一个方向上产生延迟,但不会在另一个方向上产生延迟。
2013年

84
这是一个非常懒惰的解决方案。我假设OP希望使用height:auto,因为容器的扩展高度有些不可预测。此解决方案将导致动画可见之前的延迟。另外,动画的可见持续时间将是不可预测的。通过计算每个容器子节点的组合高度,然后减小到一个精确的高度值,您将获得更加可预测的(并且可能更平滑)的结果。
肖恩·惠纳

314

您应该改用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>

我做了上述代码的供应商前缀版本的jsfiddle,并改变了你的jsfiddle到使用的scaleY,而不是高度。


6
我赞成这个答案,因为它在支持css3转换的浏览器中效果很好,但是应该注意,这在IE <9中根本不起作用,并且在IE <10中不会动画化(它只会跳转)。
Hailwood

215
该方法仅部分实现了所需的效果,但实际上并未删除空间。变换后的盒子就像一个相对定位的元素-不管如何缩放,都会占用空间。看看这个jsFiddle,它将带您的第一个,并在底部添加一些伪造的文本。请注意,将框高度缩放为零时,其下方的文本不会向上移动。
animuson

9
现在可以了:jsfiddle.net/gNDX3/1基本上,您需要根据需要对元素进行样式设置。CSS / HTML中没有灵丹妙药或类似行为的小部件。
dotnetCarpenter 2013年

70
尽管我为尝试不同方法的人鼓掌,但此解决方案带来的实际效果和复杂性远不如已经糟糕的最大高度破解。请不要使用。
mystrdat

2
@SquareCat好吧好吧。这是一个更像抽屉的抽屉:jsfiddle.net/dotnetCarpenter/WTL2r
dotnetCarpenter 2015年

202

当涉及的高度之一为时,您当前无法在高度上设置动画auto,您必须设置两个显式的高度。


12
通常有多种方法可以解决问题,但并非所有方法都是适当或可行的。我不知道为什么@JedidiahHurt和Ryan Ore试图限制问答网站上的可能答案。首先要特别考虑这个答案。令人怀疑的是,既然可接受的答案是一种解决方法。
Hooray Im帮助

5
@jake的答案既不优雅也不正确。链接的答案要好得多。它可以正常工作并处理所有大小的案件-无法说出已接受的答案。
GraphicsMuncher

5
是的:确实如此this.$element[dimension](this.$element[dimension]())[0].offsetHeight
Andy

4
是否计划以某种方式解决此问题?我的意思是,很自然地期望能够从0高度过渡到auto……
Augustin Riedinger

6
@Meliodas“不/您不能”是对堆栈溢出问题的有效且可接受的答案。
TylerH

122

我一直使用的解决方案是第一个淡出,然后收缩font-sizepaddingmargin值。它看起来与抹布并不相同,但是在没有static height或的情况下仍可以使用max-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>


为我整洁地工作。不太像jQuery slideUp / Down。您只能看到较弱的CPU(例如旧计算机或移动设备)并同时打开和关闭许多CPU的区别。
Cruz Nunez

3
如果您有按钮或任何其他非文本元素,则在不悬停时会出现不需要的空格。
丹尼斯W

3
您可以通过选择所有子元素*并应用其他更改来进一步优化它。但是,如果按钮的样式正确,则按钮应该受到我上面的代码的影响em
史蒂文·瓦雄

1
就我而言,我还需要添加line-height: 0;border-width: 0;
安德烈Shatilov

1
你不应该需要调整line-height,如果你正确地避免在值单位:developer.mozilla.org/en-US/docs/Web/CSS/...
史蒂芬瓦尚

93

我知道这是这个问题的答案,但我认为这是值得的,所以就去吧。这是具有以下属性的纯CSS解决方案:

  • 一开始没有延迟,过渡也不会尽早停止。在两个方向(展开和折叠)中,如果您在CSS中指定了300ms的过渡持续时间,则过渡需要300ms的时间。
  • 它会转换实际高度(与不同transform: scaleY(0)),因此如果可折叠元素后面有内容,它将做正确的事情。
  • 虽然(像其他的解决方案)也神奇数字(如“选择一个长度比你的框永远将会是更高的”),这不是致命的,如果你的假设最终被错。在那种情况下,过渡可能看起来并不令人惊奇,但是在过渡之前和之后,这都不成问题:在expanded(height: auto)状态下,整个内容始终具有正确的高度(例如,如果您选择的max-height是太低)。在折叠状态下,高度应为零。

演示版

这是一个具有三个可折叠元素的演示,它们的高度都不同,都使用相同的CSS。您可能要在单击“运行摘要”后单击“整页”。请注意,JavaScript仅切换collapsedCSS类,不涉及任何度量。(您可以使用复选框或来进行完全没有任何JavaScript的演示,:target)。还要注意,CSS中负责过渡的部分非常短,而HTML仅需要一个附加的wrapper元素。

$(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-bottom0px(处于展开状态)转换-2000px为折叠状态(类似于此答案)。这里的2000是第一个魔幻数字,它基于您的框不会高于此数字的假设(2000像素似乎是一个合理的选择)。

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

  • 如果您实际上有一个高于2000像素的盒子,那么它margin-bottom: -2000px不会隐藏所有内容-即使在折叠的情况下也会有可见的东西。这是一个较小的修复程序,我们将在以后进行。
  • 例如,如果实际的框高为1000像素,并且过渡的长度为300ms,则可见过渡在大约150ms后已经结束(或者,在相反的方向,延迟时间为150ms)。

解决第二个问题是第二个转换的到来,这个转换在概念上针对包装器的最小高度(“概念上”,因为我们实际上并未min-height为此使用属性;稍后将进行介绍)。

这是一个动画,它显示了如何将底部边距过渡与持续时间相等的最小高度过渡组合在一起,从而为我们提供了从全高到持续时间相同的零高度的组合过渡。

如上所述的动画

左栏显示负底边距如何向上推动底部,从而减小可见高度。中间的条显示了最小高度如何确保在折叠情况下过渡不会提前结束,而在扩展情况下过渡不会迟到。右栏显示了两者的组合如何导致框在正确的时间内从全高过渡到零高。

对于我的演示,我将50px设置为最小最小高度的上限。这是第二个魔术数字,它应该低于盒子的高度。50px也很合理;您似乎不太可能经常想使一个可折叠的元素起初甚至不超过50像素。

正如您在动画中所看到的,最终的过渡是连续的,但不可区分-在最小高度等于由下边距调整的全高度的那一刻,速度会突然变化。这在动画中非常明显,因为它对两个过渡都使用线性计时功能,并且因为整个过渡非常慢。在实际情况下(我的演示在顶部),过渡仅花费300ms,底部边距过渡不是线性的。对于这两种转换,我都使用了许多不同的计时功能,而最终我觉得它们在最广泛的情况下效果最好。

还有两个问题需要解决:

  1. 从上方的角度来看,高度超过2000像素的框在折叠状态下并未完全隐藏,
  2. 相反的问题是,在非隐藏情况下,即使不运行过渡,小于50像素高度的框也会过高,因为最小高度将其保持在50像素。

我们通过max-height: 0在折叠的情况下给容器元素a 加上0s 0.3s过渡来解决第一个问题。这意味着它并不是真正的过渡,但是max-height会延迟应用。它仅在过渡结束后才适用。为了使其正常工作,我们还需要max-height为相反的,未折叠的状态选择一个数值。但是与2000px情况不同的是,选择太大的数字会影响过渡的质量,在这种情况下,这实际上并不重要。因此,我们可以选择一个很高的数字,以至于我们知道没有一个高度会接近这个数字。我选了一百万像素。如果您觉得可能需要支持超过一百万像素的高度的内容,则1)对不起,2)仅添加两个零。

第二个问题是为什么我们实际上没有使用min-height最小高度过渡的原因。相反,::after容器中有一个伪元素,其伪元素height从50px过渡到零。这具有与相同的效果min-height:它不会让容器缩小到伪元素当前具有的任何高度以下。但是因为我们使用height,而不是min-height,所以我们现在可以使用max-height(再次应用延迟),一旦过渡结束,将伪元素的实际高度设置为零,从而确保至少在过渡之外,即使是很小的元素也具有正确的高度。因为min-height它比强大max-height,如果我们使用容器的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过渡这一事实之外,这是一个毫无意义的练习。


35
我希望您可以缩短(简化)您的解决方案,或者至少缩短示例代码-看起来90%的示例代码与您的答案无关。
Gil Epshtain '18

如果collapsible-wrapper具有绝对位置,是否有办法使这些过渡也起作用。该overflow: hidden父是必需的过渡但绝对定位元素干涉。
pmkro

1
@pmkro是的,我也遇到了这个问题。我使用a clip-path: polygon(...)代替来解决它overflow: hidden,因为与后者不同,您可以转换前者。作为旧版浏览器的后备,我使用了额外的缩放转换。请参阅github.com/StackExchange/Stacks/blob/develop/lib/css/components/…
balpha

2
真好 这真的很好。应该是被接受的答案
Henry Ing-Simmons

83

您可以用一点点非语义的拼图扑克。我通常的方法是为外部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>

一个人只希望能够放弃,.measuringWrapper而只是将DIV的高度设置为自动并设置动画,但这似乎不起作用(设置了高度,但没有动画发生)。

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>

我的解释是动画运行需要明确的高度。当高度(开始高度或结束高度)为时,您将无法获得高度动画auto


10
由于这依赖于javascript,因此您也可以使用javascript轻松添加measurementWrapper!
Quickredfox

18
您可以不用包装器就可以做到。只是:function growDiv(){var growDiv = document.getElementById('grow'); 如果(growDiv.clientHeight){growDiv.style.height = 0; } else {growDiv.style.height = growDiv.scrollHeight +'px'; }
1742529年

8
检查一下...谈论一个简单的答案jsfiddle.net/n5XfG/2596 ...您的答案非常复杂,没有充分的理由。不需要max-height,不需要auto,尤其是不需要Java语言!!仅有两个简单的声明
VIDesignz

12
@VIDesignz您为边距设置动画:100%。这是元素宽度的100%,而不是它的高度!这意味着,如果元素的高度大于其宽度,则不会隐藏它。另外,实际的动画速度与定义的速度完全不同。
TimoTürschmann'16

3
我对VIDesignz的回答感到很兴奋,直到我阅读了有关Timo的评论的更多内容。是的,margin-top(和margin-bottom)相对于宽度(以百分比定义)是相对的,这很愚蠢,但这也解释了为什么打开和关闭动画的时机完全不同-这与您在最高答案中看到的一样,并指定了硬编码的最大高度。为什么这么难做对吗?
Coderer

52

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

您还不能完全获得擦除效果,但是尝试过渡时间和填充值应该可以使您足够接近。如果您不想显式设置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/(从stephband的jsFiddle上方摘下来)


3
我认为这是最好的答案。该技术也可以扩展到调整子项上的填充。在我的情况下,我能够将其与要显示的某些内容上的显式高度转换结合起来,并且总效果比最大高度变通方法要成功得多,最大变通方法对于显示来说是很好的,但会带来一个尴尬的时刻。延迟皮革。我会完全没有动画,除非引入无意义的延迟,使我的应用似乎无响应。我很惊讶这么多人似乎认为这是可以接受的。
分号

17
除了这里,您根本没有设置动画高度。您正在为填充动画设置动画……它可以消失,因为它可以从当前状态变为0进行动画处理,但是如果您仔细观察它的展开,它会随着文本弹出,然后填充动画,因为它没有动画。不知道如何从0动画到自动。...它需要一个数字范围...这就是补间的工作原理。
罗布R

这是一个不错的解决方法,而且不易破解。这不是这个问题的最佳答案,但是它对我有用。有时您只需要一些高度动画的效果,而不是整个动画的效果:)
Ivan Durst

使用过渡延迟时,该解决方案也会失败。
米歇尔·乔尼瑟斯

我也同意这种解决方案是一种干净的解决方法,如果您的动画足够快,则可以提供非常流畅的过渡效果(对于我来说,过渡为0.1s的手风琴是完美的!)。虽然它不会提供很多应用程序,但是其他人都不会解决这个问题!
Remi D

46

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


14
@ Derija93使用的是普通的旧javascript超时动画。挑战是改为使用CSS3过渡。
2012年

3
嗯,是。但是,如果在您的示例中,如果您已经在使用jQuery(一个提供很多“少写,多做”代码的库),为什么还要使用“ CSS3过渡”呢?我想指出的是,即使不使用jQuery,您的版本也可能看起来好很多,即使它更为复杂,因此它几乎可以在任何网站上运行。我想我的措辞很不对劲。不好意思。;)
Kiruse 2012年

27
@ Derija93也许是因为CSS3过渡(根据我能找到的所有资源)比jQuery动画具有更好的性能。(实际上对于我当前的用例而言,意义重大,这使我来到了这里。)
Mark Amery 2012年

4
@ Derija93'因为与CSS3过渡相比,Javascript动画运行缓慢,对于jQuery动画,您必须担心动画循环。单击动画,然后快速单击并观看动画重复。这由CSS3处理。
AlbertEngelB

2
@ Dropped.on.Caprica现在有点老了。我已经说过我误解了他试图证明的概念。无论如何,对于简单的动画,.stop可以解决相当复杂的动画循环问题。现在,当您设置高度和颜色的动画但只希望中断高度动画时,事情会变得有些棘手...我的意思是正确的。
基拉兹(Kiruse)

43

可接受的答案在大多数情况下都可以使用,但是当您的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>


这可能会有所帮助,但是我不能说,因为没有更好的例子,我不知道如何使用它。
伊万·杜斯特

1
您只需传入任何DOM元素(例如from document.getElementById('mydiv')$('#mydiv').get()),该函数就会在隐藏/显示之间切换。如果设置CSS过渡,则该元素将自动设置动画。
Oleg Vaskevich 2015年

我添加了一个片段。这样的优点是可以很好地处理动态大小的内容。
Oleg Vaskevich

3
-1,因为这不是“使用CSS”。问题的重点是无JS解决方案。我认为,“正确”的答案不是黑客,但是……
dudewad

16
拒绝使用JS-认真吗?这里一半的答案使用JS,因此您的否决票似乎不公平或没有必要。此外,这个答案仍然是使用CSS来执行实际的动画,这是OP的重点。JS的唯一用途是计算实际高度,因为使用纯CSS 不可能做到这一点(并且min-height如预期答案中的设置会导致列表大小可能相差很大时动画速度出现问题)。
Oleg Vaskevich

30

几乎没有提及该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的未来修订版能够满足作者的更优雅的处理需求-重新审视“ compute”与“ used”和“ resolved”值的概念,并考虑是否应将过渡应用于计算值,包括与width和的转换height(目前有一些特殊处理)。


6
您在其他答案中没有找到它,因为使用该Element.scrollHeight属性与DOM进行交互需要JavaScript,并且正如该问题明确指出的那样,他们希望在没有 JavaScript的情况下执行此操作。
TylerH '16

3
除了这两个伪类之外,CSS还有很多其他部分可以解决此问题,如第1页上的最高答案所示。在JavaScript中设置样式不是“纯CSS”,因为它首先需要设置JS。通常,当一个人要求仅使用CSS的解决方案时,这是因为他们无法注入任何JavaScript,或者他们当前的项目或角色不允许这样做。
TylerH '16

29

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/


12
这里的问题是,要确保在动态环境中有足够的空间,您需要使用可笑的max-heights 999999px。另一方面,这会移动动画,因为帧是根据此值计算的。因此,您可能最终会在一个方向上出现动画“延迟”,而在另一个方向上出现一个非常快的结束(错误:计时功能)
朱利安·F·韦纳特

29

没有硬编码的值。

没有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>


做得很好-这是对实际上不应该存在的问题的有效解决方案。除非将过渡设置为“线性”,否则设置动画最大高度为基础,这对于动画设置高度可变的CMS内容也非常(显然)非常不利。
M_Willett

这很聪明,但是我不愿意为此复制DOM元素和内容。
安德烈·菲格雷多

通过设置动画transform: scaleY(1),您可以在没有容器且性能更高的情况下执行此操作,因为此过渡不会删除空间。
Fabian von Ellerts

21

发布此帖子时,已经有30多个答案,但是我觉得我的答案比jake 早已接受的答案有所改善。

我不满足于仅由使用max-height和CSS3转换引起的问题,因为正如许多评论者所指出的那样,您必须将max-height值设置为非常接近实际高度,否则会出现延迟。有关该问题的示例,请参见此JSFiddle

为了解决这个问题(尽管仍然不使用JavaScript),我添加了另一个HTML元素来转换transform: translateYCSS值。

这意味着同时使用max-heighttranslateYmax-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>


不错的比较@davidtaubmann,您的笔很好地说明了差异!我translateY结束了,scale因为我不喜欢如何产生scale这种压缩效果。
安德鲁·梅西耶

谢谢@ andrew-messier,我删除了评论,因为有一个错误,在codepen.io/davidtaubmann/pen/zKZvGP中不执行translateY(因为没有孩子去做,需要解决)……但是我们可以从ONLY MAX-HEIGHT中完美地看到比较并将其与比例混合(如其他答案所述):codepen.io/davidtaubmann/pen/jrdPER。这一切对我使用的Target方法都大有帮助
-DavidTaubmann

18

好的,我想我想出了一个非常简单的答案...不max-height,使用relative定位,在li元素上工作,并且是纯CSS。除了Firefox之外,我没有在其他任何软件上进行过测试,尽管从CSS来看,它应该可以在所有浏览器上使用。

内容:http : //jsfiddle.net/n5XfG/2596/

的CSS

.wrap { overflow:hidden; }

.inner {
            margin-top:-100%;
    -webkit-transition:margin-top 500ms;
            transition:margin-top 500ms;
}

.inner.open { margin-top:0px; }

的HTML

<div class="wrap">
    <div class="inner">Some Cool Content</div>
</div>

13
这将一直工作到元素的高度超过其宽度为止。这是使用百分比计算边距的基础:它们是根据元素的宽度计算的。因此,如果您有1000 px宽的元素,那么1100 px处的元素将太大而无法使用此解决方案,这意味着您必须增加该负的上边距。基本上,这是与使用height或max-height完全相同的问题。
dudewad

15

编辑:向下滚动以获取更新的答案,
我正在制作一个下拉列表,并看到了此帖子...许多不同的答案,但我决定也分享我的下拉列表,...这并不完美,但至少它将仅使用CSS作为落下!我一直在使用transform:translateY(y)将列表转换为视图...
您可以在测试中看到更多信息
http://jsfiddle.net/BVEpc/4/
我将div放在每个li后面是因为下拉列表是从上而来的,为了正确显示它们是必需的,我的div代码是:

#menu div {
    transition: 0.5s 1s;
    z-index:-1;
    -webkit-transform:translateY(-100%);
    -webkit-transform-origin: top;
}

悬停是:

#menu > li:hover div {
    transition: 0.5s;
    -webkit-transform:translateY(0);
}

并且因为ul height设置为内容,所以它可以覆盖您的身体内容,这就是我为ul执行此操作的原因:

 #menu ul {
    transition: 0s 1.5s;
    visibility:hidden;
    overflow:hidden;
}

并悬停:

#menu > li:hover ul {
     transition:none;
     visibility:visible;
}

过渡之后的第二次延迟,它会在我的下拉列表被动画关闭后隐藏起来。
希望以后有人能从中受益。

编辑:我简直不敢相信ppl实际上使用了这个原型!该下拉菜单仅适用于一个子菜单,仅此而已!!我更新了一个更好的版本,可以在支持IE 8的同时为ltr和rtl方向提供两个子菜单。
LTR的
Fiddle的RTL的Fiddle的
希望,以后有人会觉得有用。


11

您可以从height:0转换为height:auto,前提是您还要提供min-height和max-height。

div.stretchy{
    transition: 1s linear;
}

div.stretchy.hidden{
    height: 0;
}

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

@Stuart羽毛球能否提供工作示例?我把它放在一起,但似乎在任何地方都不起作用:( jsfiddle.net/gryzzly/n5XfG
Misha Reyzlin 2011年

5
问题出在您的过渡规则上。为了使其正常工作,您还需要将过渡应用于最小和最大高度。过渡:高度.5s线性,最小高度.5s线性,最大高度.5s线性
斯图尔特羽毛球

2
正如您所说,这是我稍后要设置的动画最大高度的方法:jsfiddle.net/gryzzly/n5XfG/3
Misha Reyzlin 2011年

11
当然,这是有问题的。为max-height设置动画所需的时间不是过渡到框的自动高度所花费的时间:它到达height:在max-height完成动画制作之前首先到达auto的时间。同样,过渡到0时,过渡不会立即开始,因为max-height从大于height:auto的高度开始。过渡到最大高度:1000px
stephband,2011年

您是正确的,在进一步测试中,我确实注意到了这一点。我不知道是否可以实现全高:0到height:自动,但是在某些情况下使用max-height确实可以解决仅使用CSS的其他情况。
斯图尔特·羽毛球

10

Flexbox解决方案

优点:

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

缺点:

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

它的工作方式是始终具有flex-basis:在具有内容的元素上自动放置,然后过渡flex-grow和flex-shrink。

编辑:受Xbox One界面启发的改进的JS Fiddle。

* {
  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小提琴


7

扩展@jake的答案,过渡将一直达到最大高度值,从而产生非常快的动画-如果同时设置:hover和off的过渡,则可以控制疯狂速度。

所以li:hover是鼠标进入状态时的状态,然后非悬停属性上的过渡将是鼠标离开。

希望这会有所帮助。

例如:

.sidemenu li ul {
   max-height: 0px;
   -webkit-transition: all .3s ease;
   -moz-transition: all .3s ease;
   -o-transition: all .3s ease;
   -ms-transition: all .3s ease;
   transition: all .3s ease;
}
.sidemenu li:hover ul {
    max-height: 500px;
    -webkit-transition: all 1s ease;
   -moz-transition: all 1s ease;
   -o-transition: all 1s ease;
   -ms-transition: all 1s ease;
   transition: all 1s ease;
}
/* Adjust speeds to the possible height of the list */

这是一个小提琴:http : //jsfiddle.net/BukwJ/


1
我不知道为什么得到-1。实际上,我认为这是比一些添加JavaScript负载的人更好的尝试。这不是一个完美的解决方案,但是通过一些调整它做得很好,我发现这对于返回值很有效。过渡:全部500ms立方贝塞尔(0.000,1.225,0.085,0.385); 这是基于打开时的2s转换,然后上面的代码返回到初始状态。谢谢...这比以上所有都对我有所帮助。+1我使用此网站创建了
贝塞尔曲线

好的,需要更多的调整。过渡:全部500ms立方贝塞尔(0.000,1.390,0.000,0.635); 我应该指定我的过渡范围为5%-100%(但实际上最终价值约为28%),所以这取决于项目。
gcoulby 2014年

绝对是不理想的,但是如果您知道平均高度是多少,则可以快速解决。
jamie

7

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

好!我知道这个问题与互联网一样古老,但是我认为我有一个解决方案,后来我将其变成了名为mutant-transition插件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 +中转换。

小提琴!


12
通常,当有人问“不使用JavaScript”时如何做某事时,这意味着该解决方案必须在禁用了JavaScript的浏览器上工作。这并不意味着“不编写我自己的脚本”。因此,可以说无需使用JavaScript就可以使用您的插件充其量是一种误导-考虑到它是一个JavaScript插件。
BoltClock

我应该澄清一下,当我说您不必使用任何特殊的javascript时,我的意思是您不必编写任何javascript。只需包含JS库并指定要在HTML中观看的属性即可。您不必使用固定高度的CSS,也无需弄清楚任何东西。只是样式和去。
JonTroncoso

“(...)您实际上并不需要设置任何内容或使用javascript手动设置事物的动画(...)”,因此,您在使用JavaScript很有趣
Roko C. Buljan

6

这是一种从任何起始高度(包括0)过渡到自动(完整大小和灵活)的方式,而无需在每个节点上设置硬代码或初始化任何用户代码:https : //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 Just™全部使用

/ *高度代码:自动;过渡* /

(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);

/ *演示代码* /

    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);

9
尽管这是比建议的其他解决方案更加干净和灵活的解决方案,但我个人认为这绝对不是JS应该接触的东西。并不是说你错了,这只是一个叹息。
mystrdat 2013年

@mystrdat需要明确,但是JS仅用于监视某些条件,并根据要监视的事件/条件添加几个属性/临时CSS值。过渡仍然全部在CSS中完成。尽管我同意,但令人遗憾的是,要真正满足看似简单的用例,就必须有这样的设置水平和手动操作!:/
csuwldcat

1
@KarolisRamanauskas不,这不是问题-IE支持CSS关键帧和requestAnimationFrame。我当时使用clip我的虚拟属性触发器,由于某种原因,IE停止允许剪辑动画。我切换到不透明度,并且所有这些都再次起作用:codepen.io/csuwldcat/pen/FpzGa。我已经更新了答案中的代码以使其匹配。
csuwldcat

1
@csuwldcat非常好的解决方案,我注意到内容只出现一小部分就消失了,为什么会这样?可以避免吗?
Beto Aveiga 2014年

12
我想对此表示赞同,但是您没有回答如何使它工作的问题,而是共享了一个编写的插件以使其工作。好奇的是,我们不得不对您的插件进行反向工程,这没什么好玩的。我希望您能更新您的答案以包含有关您的插件功能以及原因的更多说明。更改代码以使其更具解释性。例如,您有一整段代码只是写出静态CSS。我宁愿看到CSS,而不是生成它的代码。您可以忽略无聊的部分,例如重复所有浏览器前缀。
gilly3 2015年

6

杰克(Jake)设置最大高度动画的答案很棒,但是我发现设置较大的最大高度烦人会造成延迟。

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

$('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/duZpT

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


5

我意识到该线程已经过时,但是在某些Google搜索中它的排名很高,因此我认为值得进行更新。

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

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

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


document.getElementById('target_box')。getBoundingClientRect()。height应该最好。
改变

5

我能够做到这一点。我有一个.child.parentdiv。子div可以很好地适合父母的宽度/高度absolute。然后,我对该translate属性进行动画处理以Y降低其价值100%。它的动画非常流畅,没有毛刺或缺点,就像这里的任何其他解决方案一样。

像这样的伪代码

.parent{ position:relative; overflow:hidden; } 
/** shown state */
.child {
  position:absolute;top:0;:left:0;right:0;bottom:0;
  height: 100%;
  transition: transform @overlay-animation-duration ease-in-out;
  .translate(0, 0);
}

/** Animate to hidden by sliding down: */
.child.slidedown {
  .translate(0, 100%); /** Translate the element "out" the bottom of it's .scene container "mask" so its hidden */
}

您将指定一个height.parent,中px%或休假的auto。然后,该.childdiv向下滑动时会将该div掩盖。


这个工作示例将不胜感激。
神经递质

5

我发布了一些JavaScript的答案,并被否决了,因此恼火并再次尝试,仅使用CSS破解了它!

该解决方案使用了一些“技术”:

这样做的结果是,我们仅使用CSS即可进行高效的过渡,并且可以使用一个过渡功能平稳地实现过渡。圣杯!

当然,还有一个缺点!我无法解决如何控制内容被截断的宽度(overflow:hidden);由于存在底部填充漏洞,因此宽度和高度密切相关。虽然可能有一种方法,所以会回到正题。

https://jsfiddle.net/EoghanM/n1rp3zb4/28/

body {
  padding: 1em;
}

.trigger {
  font-weight: bold;
}

/* .expander is there for float clearing purposes only */
.expander::after {
  content: '';
  display: table;
  clear: both;
}

.outer {
  float: left; /* purpose: shrink to fit content */
  border: 1px solid green;
  overflow: hidden;
}

.inner {
  transition: padding-bottom 0.3s ease-in-out;  /* or whatever crazy transition function you can come up with! */
  padding-bottom: 0%;  /* percentage padding is defined in terms of width. The width at this level is equal to the height of the content */
  height: 0;

  /* unfortunately, change of writing mode has other bad effects like orientation of cursor */
  writing-mode: vertical-rl;
  cursor: default; /* don't want the vertical-text (sideways I-beam) */
  transform: rotate(-90deg) translateX(-100%);  /* undo writing mode */
  transform-origin: 0 0;
  margin: 0;  /* left/right margins here will add to height */
}

.inner > div { white-space: nowrap; }

.expander:hover .inner,  /* to keep open when expanded */
.trigger:hover+.expander .inner {
  padding-bottom: 100%;
}
<div class="trigger">HoverMe</div>
<div class="expander">
  <div class="outer">
    <div class="inner">
      <div>First Item</div>
      <div>Content</div>
      <div>Content</div>
      <div>Content</div>
      <div>Long Content can't be wider than outer height unfortunately</div>
      <div>Last Item</div>
    </div>
  </div>
</div>
<div>
  after content</div>
</div>


另一个缺点是您不能将光标从“ hoverme” div移到项目列表以便与它们进行交互(表面上是询问OP的原因),因此它仅用作工具提示功能,而不是,例如,带有嵌套子菜单的菜单。
TylerH

这纯粹是问题提出方式的结果,与技术无关。您可以根据需要将触发器更改为,input[type="checkbox"]:checked而不是:hover(例如,您不想使用JavaScript添加任何类)
EoghanM

我的意思是,不管问题的表达方式如何,如果解决方案的实施方式如此有限,则可能应在该解决方案的答案中提及它(或者最好考虑)。您提到此解决方案是“圣杯”,但过渡后的div甚至无法悬停……对于我来说,这似乎不是一个如此最终的解决方案。
TylerH

抱歉,@ TylerH,我很抱歉,原来的问题中的触发器围绕着扩展列表,因此,将鼠标悬停在列表上时,列表应保持打开状态。我已经更新了答案以反映这一点。正如我所提到的,它的触发方式并不是那么紧密,因为我正在介绍一种以前认为仅在CSS中不可能实现的新技术(我一直在寻找解决方案已有几年了)。
EoghanM

4

这是我刚与jQuery结合使用的解决方案。这适用于以下HTML结构:

<nav id="main-nav">
    <ul>
        <li>
            <a class="main-link" href="yourlink.html">Link</a>
            <ul>
                <li><a href="yourlink.html">Sub Link</a></li>
            </ul>
        </li>
    </ul>
</nav>

和功能:

    $('#main-nav li ul').each(function(){
        $me = $(this);

        //Count the number of li elements in this UL
        var liCount = $me.find('li').size(),
        //Multiply the liCount by the height + the margin on each li
            ulHeight = liCount * 28;

        //Store height in the data-height attribute in the UL
        $me.attr("data-height", ulHeight);
    });

然后,您可以使用点击功能来设置和删除高度 css()

$('#main-nav li a.main-link').click(function(){
    //Collapse all submenus back to 0
    $('#main-nav li ul').removeAttr('style');

    $(this).parent().addClass('current');

    //Set height on current submenu to it's height
    var $currentUl = $('li.current ul'),
        currentUlHeight = $currentUl.attr('data-height');
})

CSS:

#main-nav li ul { 
    height: 0;
    position: relative;
    overflow: hidden;
    opacity: 0; 
    filter: alpha(opacity=0); 
    -ms-filter: "alpha(opacity=0)";
    -khtml-opacity: 0; 
    -moz-opacity: 0;
    -webkit-transition: all .6s ease-in-out;
    -moz-transition: all .6s ease-in-out;
    -o-transition: all .6s ease-in-out;
    -ms-transition: all .6s ease-in-out;
    transition: all .6s ease-in-out;
}

#main-nav li.current ul {
    opacity: 1.0; 
    filter: alpha(opacity=100); 
    -ms-filter: "alpha(opacity=100)";
    -khtml-opacity: 1.0; 
    -moz-opacity: 1.0;
}

.ie #main-nav li.current ul { height: auto !important }

#main-nav li { height: 25px; display: block; margin-bottom: 3px }

最新的jquery版本无法正确显示该信息
oliverbachmann 2015年

4

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

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

如果我的字体大小为1rem,则将其设置max-height3rem(以容纳换行文本)。您可以在此处查看示例:

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


3

我没有详细阅读所有内容,但是最近遇到了这个问题,我做了以下工作:

div.class{
   min-height:1%;
   max-height:200px;
   -webkit-transition: all 0.5s ease;
   -moz-transition: all 0.5s ease;
   -o-transition: all 0.5s ease;
   -webkit-transition: all 0.5s ease;
   transition: all 0.5s ease;
   overflow:hidden;
}

div.class:hover{
   min-height:100%;
   max-height:3000px;
}

这样一来,您可以创建一个div,该div首先显示最高200 px的高度,然后将其悬停,其大小至少应与div的整个内容一样高。Div不会变为3000px,但我要施加的极限是3000px。确保在non:hover上进行过渡,否则可能会得到一些奇怪的渲染。这样,:hover将从非:hover继承。

从px到%或自动的过渡均无效。您需要使用相同的度量单位。这对我来说很好。使用HTML5使其完美。

记住,总有一个解决方案……;)

希望有人觉得这有用


3

如果提供的硬编码的max-height值不大于实际高度,则Jake的max-height解决方案效果很好(因为否则会有不希望的延迟和计时问题)。另一方面,如果硬编码值偶然不大于实际高度,则该元素将不会完全打开。

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

想法是对底边距使用负值(对于稍有不同的动画,对边距顶部使用负值),然后将内容元素放入带有overflow:hidden的中间元素中。内容元素的负边距会降低中间元素的高度。

下面的代码使用从-150px到0px的margin-bottom过渡。只要content元素不高于150px,这单独就可以正常工作。此外,它对中间元素的最大高度使用了从0px到100%的过渡。如果content元素大于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>

边距底部的值应为负,并尽可能接近内容元素的实际高度。如果它的(绝对值)较大,则存在与最大高度解决方案类似的延迟和时序问题,但是,只要硬编码的大小不比实际大小大很多,就可以限制它们。如果边缘下限的绝对值小于实际高度,则过渡会略微跳跃。在过渡之后的任何情况下,内容元素都将完全显示或完全删除。

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


3

您可以通过使用剪辑路径创建反向(折叠)动画来实现此目的。

#child0 {
    display: none;
}
#parent0:hover #child0 {
    display: block;
    animation: height-animation;
    animation-duration: 200ms;
    animation-timing-function: linear;
    animation-fill-mode: backwards;
    animation-iteration-count: 1;
    animation-delay: 200ms;
}
@keyframes height-animation {
    0% {
        clip-path: polygon(0% 0%, 100% 0.00%, 100% 0%, 0% 0%);
    }
    100% {
        clip-path: polygon(0% 0%, 100% 0.00%, 100% 100%, 0% 100%);
    }
}
<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>


2

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

.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/wzjRKa

本质上,您将font-size设置为0并以足够快的速度转换它,而不是转换高度,max-height或scaleY()等,以使高度转换为所需的高度。当前无法使用CSS将实际高度转换为自动,但是可以转换其中的内容,因此可以转换字体大小。

  • 注意-在代码笔中有JavaScript,但是唯一的目的是单击时为手风琴添加/删除css类。这可以通过隐藏的单选按钮完成,但我并没有专注于此,只是高度转换。

1
有效地复制了此答案,但是省略了不透明淡入效果。
SeldomNeedy

同意 更好的方法。
Mike S.19年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.