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

发布于 2024-09-08 01:07:25 字数 1763 浏览 6 评论 0 原文

我正在尝试使用 CSS 过渡制作

    向下滑动。

    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>

I am trying to make a <ul> slide down using CSS transitions.

The <ul> starts off at height: 0;. On hover, the height is set to height:auto;. However, this is causing it to simply appear, not transition,

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.

How else could I do this without using 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>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(30

梦毁影碎の 2024-09-15 01:07:26

在过渡中使用 max-height 而不是 height。并将 max-height 的值设置为大于您的盒子所能达到的值。

请参阅 Chris Jordan 在另一个 JSFiddle 演示。 com/a/20226830/18706">在这里回答。

#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>

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.

See JSFiddle demo provided by Chris Jordan in another answer here.

#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>

哥,最终变帅啦 2024-09-15 01:07:26

您应该使用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而不是高度。

编辑
有些人不喜欢 scaleY 转换内容的方式。如果这是一个问题,那么我建议使用 clip 代替。

ul {
  clip: rect(auto, auto, 0, auto);
  position: absolute;
  margin: -1rem 0;
  padding: .5rem;

  color: white;

  background-color: rgba(0, 0, 0, 0.8);

  transition-property: clip;
  transition-duration: 0.5s;
  transition-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
h3:hover ~ ul,
h3:active ~ ul,
ul:hover {
  clip: rect(auto, auto, 10rem, auto);
}
<h3>Hover here</h3>
<ul>
  <li>This list</li>
  <li>is clipped.</li>
  <li>A clip transition</li>
  <li>will show it</li>
</ul>
<p>
  Some text...
</p>

You should use scaleY instead.

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 jsfiddle to use scaleY instead of height.

Edit
Some people do not like how scaleY transforms the content. If that is a problem then I suggest using clip instead.

ul {
  clip: rect(auto, auto, 0, auto);
  position: absolute;
  margin: -1rem 0;
  padding: .5rem;

  color: white;

  background-color: rgba(0, 0, 0, 0.8);

  transition-property: clip;
  transition-duration: 0.5s;
  transition-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
h3:hover ~ ul,
h3:active ~ ul,
ul:hover {
  clip: rect(auto, auto, 10rem, auto);
}
<h3>Hover here</h3>
<ul>
  <li>This list</li>
  <li>is clipped.</li>
  <li>A clip transition</li>
  <li>will show it</li>
</ul>
<p>
  Some text...
</p>

九歌凝 2024-09-15 01:07:26

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

You can't currently animate on height when one of the heights involved is auto, you have to set two explicit heights.

高跟鞋的旋律 2024-09-15 01:07:26

我一直使用的解决方案是首先淡出,然后缩小 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>

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.

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>

老旧海报 2024-09-15 01:07:26

这是一个纯 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 折叠状态(类似于 这个答案)。这里的 2000 是第一个幻数,它基于您的盒子不会高于此的假设(2000 像素似乎是一个合理的选择)。

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

  • 如果您实际上有一个高于 2000 像素的框,则 margin-bottom: -2000px 不会隐藏所有东西——即使在折叠的箱子里也会有可见的东西。这是我们稍后会做的一个小修复。
  • 如果实际框的高度为 1000 像素,并且过渡长度为 300 毫秒,则可见过渡在大约 150 毫秒后就已经结束(或者,在相反的方向上,晚了 150 毫秒开始)。

解决第二个问题是第二个过渡出现的地方,这个过渡在概念上以包装器的最小高度为目标(“概念上”是因为我们实际上并没有使用 min-height属性;稍后会详细介绍)。

下面的动画展示了如何将具有相同持续时间的底部边距过渡与最小高度过渡相结合,为我们提供具有相同持续时间的从全高度到零高度的组合过渡。

如上所述的动画上面

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

在我的演示中,我选择 50px 作为最小高度值上限。这是第二个神奇数字,它应该低于盒子的高度。 50px 似乎也合理;您似乎不太可能经常想要使一个高度甚至不到 50 像素的元素可折叠。

正如你在动画中看到的,产生的过渡是连续的,但它是不可微分的——当最小高度等于底部边距调整的完整高度时,速度会突然发生变化。这在动画中非常明显,因为它对两个过渡都使用线性计时函数,并且整个过渡非常慢。在实际情况中(我的演示在顶部),转换只需要 300ms,并且底部边距转换不是线性的。对于这两种转换,我尝试过很多不同的计时函数,最终我觉得它们最适合最广泛的情况。

有两个问题需要解决:

  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 而不是伪元素的 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 过渡这一事实,这使其成为一项毫无意义的练习。

This is a CSS-only solution with the following properties:

  • 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 are magic 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 a max-height that turns out to be too low). And in the collapsed state, the height is zero as it should.

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.

$(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 two transitions 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).

Using the margin-bottom transition alone by itself has two issues:

  • 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 visible transition is already over after about 150ms (or, in the opposite direction, starts 150ms late).

Fixing this second issue is where the second transition comes in, and this transition conceptually targets the wrapper's minimum height ("conceptually" because we're not actually using the min-height property for this; more on that later).

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.

animation as described above

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.

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.

Two problems remain to fix:

  1. the point from above, where boxes of more than 2000 pixels height aren't completely hidden in the collapsed state,
  2. 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.

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 know that 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.

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 stronger than 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.

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.

月牙弯弯 2024-09-15 01:07:26

你可以,用一点非语义的技巧。我通常的方法是对具有单个子项的外部 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 的高度设置为 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>

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

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.

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

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.

临走之时 2024-09-15 01:07:26

接受的答案适用于大多数情况,但当您的 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>

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.

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 :)).

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>

墨落画卷 2024-09-15 01:07:26

自从这个问题首次提出以来,浏览器已经发生了很大的变化。您现在可以转换网格轨道大小,并且它得到了很好的支持,这意味着这个问题可以通过使用 CSS 网格布局 解决

这里我正在转换第二个网格项从 0fr(0 个小数单位)到 1fr(1 个小数单位)。

#parent0 {
  display: grid;
  grid-template-rows: min-content 0fr;
  transition: grid-template-rows 500ms;
}
#child0 {   
  background-color: #dedede;
  overflow: hidden;
}
#parent0:hover{
  grid-template-rows: min-content 1fr;
}
<div id="parent0">
  <h1>Hover me</h1>
  <div id="child0">Some content
    <br>Some content
    <br>Some content
    <br>Some content
    <br>Some content
  </div>
</div>

Browsers have moved on a fair bit since this question was first raised. You can now transition grid track sizes and it is very well supported meaning that this problem can be solved using CSS Grid Layout

Here I am transitioning the second grid item from 0fr (0 Fractional Units) to 1fr (1 Fractional Units).

#parent0 {
  display: grid;
  grid-template-rows: min-content 0fr;
  transition: grid-template-rows 500ms;
}
#child0 {   
  background-color: #dedede;
  overflow: hidden;
}
#parent0:hover{
  grid-template-rows: min-content 1fr;
}
<div id="parent0">
  <h1>Hover me</h1>
  <div id="child0">Some content
    <br>Some content
    <br>Some content
    <br>Some content
    <br>Some content
  </div>
</div>

心房的律动 2024-09-15 01:07:26

我的解决方法是将 max-height 转换为精确的内容高度,以获得流畅的动画,然后使用 transitionEnd 回调来设置 max-height9999px 以便内容可以自由调整大小。

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>

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.

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>

马蹄踏│碎落叶 2024-09-15 01:07:26

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

您还没有完全获得完整的擦除效果,但是调整过渡持续时间和填充值应该可以让您足够接近。如果您不想显式设置高度/最大高度,这应该就是您正在寻找的。

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 )

A visual workaround to animating height using CSS3 transitions is to animate the padding instead.

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)

铁憨憨 2024-09-15 01:07:26

根据 MDN Web 文档 auto 值已有意从 CSS 转换规范中排除,因此不要使用 height: auto,而是使用 height: 100%, top >,或网格和 Flex 布局中的 flex 属性。

展开/折叠覆盖层

.grid-container {
  display: grid;
  position: absolute;
}

.content {
  background: aqua;
  height: 0;
  overflow: hidden;
  transition: 1s;
}

span:hover + .grid-container .content {
  height: 100%;
}
<span>Hover over me!</span>

<div class="grid-container">

  <div class="content">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</div>

</div>

<p>Rest of the page content...</p>

展开/折叠滑动覆盖层

.grid-container {
  display: grid;
  position: absolute;
  overflow: hidden;
  pointer-events: none; /* to enable interaction with elements below the container */
}

.content {
  background: aqua;
  pointer-events: auto;
  position: relative;
  top: -100%;
  transition: 1s;
}

span:hover + .grid-container .content {
  top: 0;
}
<span>Hover over me!</span>

<div class="grid-container">

  <div class="content">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</div>

</div>

<p>Rest of the page content...</p>

According to MDN Web Docs, auto values have been intentionally excluded from the CSS transitions spec, so instead of height: auto, use height: 100%, top, or the flex property in grid and flex layouts.

Expanding/collapsing an overlay

.grid-container {
  display: grid;
  position: absolute;
}

.content {
  background: aqua;
  height: 0;
  overflow: hidden;
  transition: 1s;
}

span:hover + .grid-container .content {
  height: 100%;
}
<span>Hover over me!</span>

<div class="grid-container">

  <div class="content">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</div>

</div>

<p>Rest of the page content...</p>

Expanding/collapsing a sliding overlay

.grid-container {
  display: grid;
  position: absolute;
  overflow: hidden;
  pointer-events: none; /* to enable interaction with elements below the container */
}

.content {
  background: aqua;
  pointer-events: auto;
  position: relative;
  top: -100%;
  transition: 1s;
}

span:hover + .grid-container .content {
  top: 0;
}
<span>Hover over me!</span>

<div class="grid-container">

  <div class="content">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</div>

</div>

<p>Rest of the page content...</p>

来世叙缘 2024-09-15 01:07:26

使用 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/

Use max-height with different transition easing and delay for each state.

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

See example: http://jsfiddle.net/0hnjehjc/1/

半﹌身腐败 2024-09-15 01:07:26

没有硬编码值。

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

No hard coded values.

No JavaScript.

No approximations.

The trick is to use a hidden & duplicated div to get the browser to understand what 100% means.

This method is suitable whenever you're able to duplicate the DOM of the element you wish to animate.

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

眉目亦如画i 2024-09-15 01:07:26

更新,2024

我不推荐我原来的回复,但我会把它留给后代。自从 flexbox 得到广泛支持以来,我一直依赖在此问题上寻找解决方案,例如本问题中的问题。

我推荐这个答案,它使用弹性容器以及 height: 0 和 < code>height: 100% 以避免任何类型的“魔法值”。

原始(已过时),来自 2016 年

当我发布这篇文章时,已经有超过 30 个答案,但我觉得我的答案在已经已接受的答案< /a> 作者:杰克。

我对仅仅使用 max-height 和 CSS3 过渡所产生的问题并不满意,因为正如许多评论者指出的那样,您必须将 max-height 值设置得非常接近到实际高度,否则你会得到延迟。有关该问题的示例,请参阅 JSFiddle

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

这意味着同时使用 max-heighttranslateYmax-height 允许元素下推其下方的元素,而 translateY 给出了我们想要的“即时”效果。 max-height 的问题仍然存在,但其影响有所减弱。
这意味着您可以为 max-height 值设置更大的高度,而不必担心它。

总体好处是,在返回(折叠)时,用户会立即看到 translateY 动画,因此 max-height 的长度并不重要。需要。

Fiddle 解决方案

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>

Update, 2024

I do not recommend my original response, but I will leave it for posterity. Ever since flexbox became widely supported, I have relied on it for solutions to issues like the one in this question.

I recommend this answer, which uses a flex container and a transition between height: 0 and height: 100% to avoid any sort of "magic values".

Original (outdated), from 2016

As I post this there are over 30 answers already, but I feel my answer improves on the already accepted answer by jake.

I was not content with the issue that arises from simply using max-height and CSS3 transitions, since as many commenters noted, you have to set your max-height value very close to the actual height or you'll get a delay. See this JSFiddle for an example of that problem.

To get around this (while still using no JavaScript), I added another HTML element that transitions the transform: translateY CSS value.

This means both max-height and translateY are used: max-height allows the element to push down elements below it, while translateY gives the "instant" effect we want. The issue with max-height still exists, but its effect is lessened.
This means you can set a much larger height for your max-height value and worry about it less.

The overall benefit is that on the transition back in (the collapse), the user sees the translateY animation immediately, so it doesn't really matter how long the max-height takes.

Solution as Fiddle

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>

囍孤女 2024-09-15 01:07:26

max-height,使用 relative 定位,适用于 li 元素,&是纯 CSS:

除了 Firefox 之外,我没有在任何浏览器中进行过测试,但从 CSS 来看,它应该适用于所有浏览器。

FIDDLE: 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>

No max-height, uses relative positioning, works on li elements, & is pure CSS:

I have not tested in anything but Firefox, though judging by the CSS, it should work on all browsers.

FIDDLE: 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>
扛起拖把扫天下 2024-09-15 01:07:26

很多答案,有些比其他更好,大多数使用 JS。我相信我通过两个易于理解的用例解决了这个问题。

切换覆盖

.demo01 {
  overflow: hidden;
  position: absolute;
  pointer-events: none;
}
.demo01__content {
  background: lightgray;
  padding: 1rem;
  pointer-events: all;
  transform: translateY(-100%);
  transition: transform 1s, visibility 1s;
  visibility: hidden;
}
:checked ~ .demo01 .demo01__content {
  transform: translateY(0);
  visibility: visible;
}
<input type="checkbox" /> ⬅︎ Toggle
<div>Something before

A lot of answers, some better than other, most using JS. I believe I figured this out in two use-cases that are easy to understand.

Toggle an overlay

.demo01 {
  overflow: hidden;
  position: absolute;
  pointer-events: none;
}
.demo01__content {
  background: lightgray;
  padding: 1rem;
  pointer-events: all;
  transform: translateY(-100%);
  transition: transform 1s, visibility 1s;
  visibility: hidden;
}
:checked ~ .demo01 .demo01__content {
  transform: translateY(0);
  visibility: visible;
}
<input type="checkbox" /> ⬅︎ Toggle
<div>Something before ????</div>
<div class="demo01">
  <div class="demo01__content">
    This content should…<br />
    toggle! ????
  </div>
</div>
<div>Something after ????</div>

Toggle in the document flow

.demo02 {
  display: grid;
  grid-template-rows: 0fr;
  overflow: hidden;
  transition: grid-template-rows 1s;
}
.demo02__content {
  align-self: end;
  min-height: 0;
  background: lightgray;
  transition: visibility 1s;
  visibility: hidden;
}
.demo02__padding {
  padding: 1rem;
}
:checked ~ .demo02 {
  grid-template-rows: 1fr;
}
:checked ~ .demo02 .demo02__content {
  visibility: visible;
}
<input type="checkbox" /> ⬅︎ Toggle
<div>Something before ????</div>
<div class="demo02">
  <div class="demo02__content">
    <div class="demo02__padding">
      This content should…<br />
      toggle! ????
    </div>
  </div>
</div>
<div>Something after ????</div>

I wrote a blog post about these techniques.

如歌彻婉言 2024-09-15 01:07:26

编辑:向下滚动以获取更新的答案
我正在制作一个下拉列表,看到了这篇文章...有很多不同的答案,但我决定也分享我的下拉列表,...它并不完美,但至少它只会使用 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 高度设置为它可以越过你的身体的内容content 这就是为什么我对 ul:

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

和悬停:

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

转换后的第二次是延迟,并且在我的下拉列表被动画关闭后它将被隐藏......
希望以后有人能受益于此。

编辑:我简直不敢相信人们真的使用这个原型!此下拉菜单仅适用于一个子菜单,仅此而已!
我更新了一个更好的菜单,它可以有两个子菜单,用于 ltr 和 rtl 方向,并支持 IE 8。
LTR 小提琴
RTL 小提琴
希望将来有人发现这很有用。

EDIT: Scroll down for updated answer
I was making a drop down list and saw this Post ... many different answers but I decide to share my drop down list too, ... It's not perfect but at least it will using only css for drop down! I've been using transform:translateY(y) to transform the list to the view ...
You can see more in the test
http://jsfiddle.net/BVEpc/4/
I've placed div behind every li because my drop down list are coming from up and to show them properly this was needed, my div code is:

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

and hover is :

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

and because ul height is set to the content it can get over your body content that's why I did this for ul:

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

and hover:

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

the second time after transition is delay and it will get hidden after my drop down list has been closed animately ...
Hope later someone get benefit of this one.

EDIT: I just can't believe ppl actually using this prototype! this drop down menu is only for one sub menu and that's all!!
I've updated a better one that can have two sub menu for both ltr and rtl direction with IE 8 support.
Fiddle for LTR
Fiddle for RTL
hopefully someone find this useful in future.

终陌 2024-09-15 01:07:26

Flexbox 解决方案

优点:

  • 简单,
  • 无 JS
  • 平滑过渡

缺点:

  • 元素需要放入固定高度的 Flex 容器中

它的工作方式是始终在包含内容的元素上使用 flex-basis: auto 并进行过渡而是使用 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 小提琴

Flexbox Solution

Pros:

  • simple
  • no JS
  • smooth transition

Cons:

  • element needs to be put in a fixed height flex container

The way it works is by always having flex-basis: auto on the element with content, and transitioning flex-grow and flex-shrink instead.

Edit: Improved JS Fiddle inspired by the Xbox One interface.

* {
  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 Fiddle

甜是你 2024-09-15 01:07:26

仅使用灵活高度 CSS 的解决方案

我偶然发现了一个使用 Flex 行为的奇怪解决方案。它至少可以在 Chrome 和 Firefox 中运行。

  • 首先,高度过渡仅在0100%之间起作用,两个
    数值。由于“auto”不是数值,因此是小数
    0 和“auto”之间不存在增量。 100%是灵活的
    值,因此不需要特定的高度。

  • 其次,隐藏内容的外容器和内容器都必须设置为 display: flexflex-direction: column

  • 第三,外容器必须有高度属性。将其设置为0仅当所有内容都包含在外部容器中时才能保持平滑过渡,因为伸缩行为优先于高度。 编辑Paintoshi建议使用height: fit-content,所以容器下方的任何内容也会被下推。

.outer-container { height: 0; display: flex; flex-direction: column; }

.inner-container { display: flex; flex-direction: column; }

.hidden-content { 
    height: 0; 
    opacity: 0; 
    transition: height 1s 0.5s ease-in-out, opacity 0.5s ease-in-out;
    /* transition out: first fade out opacity, then shrink height after a delay equal to the opacity duration */
    }

.trigger:hover + .inner-container > .hidden-content { 
    height: 100%; 
    opacity: 1; 
    transition: height 1s ease-in-out, opacity 0.5s 1s ease-in-out;
    /* transition in: first expand height, then fade in opacity after a delay equal to the height duration */
}
<div class="outer-container">
  <a href="#" class="trigger">Hover to Reveal Inner Container's Hidden Content</a>
  <div class="inner-container">
    <div class="hidden-content">This is hidden content. When triggered by hover, its height transitions from 0 to 100%, which pushes other content in the same container down gradually.</div>
    <div>Within the same container, this other content is pushed down gradually as the hidden content's height transitions from 0 to 100%.</div>
  </div>
</div>

按“运行代码片段”按钮查看正在运行的转换。
它只是 CSS,没有具体的高度。

Flexible Height CSS Only Solution

I've stumbled upon a quirky solution using flex behavior. It works in at least Chrome and Firefox.

  • First, the height transition only works between 0 and 100%, two
    numeric values. Since "auto" is not a numeric value, fractional
    increments don't exist between 0 and "auto". 100% is a flexible
    value, so no specific height is required.

  • Second, both the outer container and the inner container of the hidden content must be set to display: flex with flex-direction: column.

  • Third, the outer container must have a height property. Setting it to 0 maintains a smooth transition only when everything is contained in the outer container because the flex behavior takes precedence over the height. Edit: Paintoshi suggested using height: fit-content, so that any content below the container is also pushed down.

.outer-container { height: 0; display: flex; flex-direction: column; }

.inner-container { display: flex; flex-direction: column; }

.hidden-content { 
    height: 0; 
    opacity: 0; 
    transition: height 1s 0.5s ease-in-out, opacity 0.5s ease-in-out;
    /* transition out: first fade out opacity, then shrink height after a delay equal to the opacity duration */
    }

.trigger:hover + .inner-container > .hidden-content { 
    height: 100%; 
    opacity: 1; 
    transition: height 1s ease-in-out, opacity 0.5s 1s ease-in-out;
    /* transition in: first expand height, then fade in opacity after a delay equal to the height duration */
}
<div class="outer-container">
  <a href="#" class="trigger">Hover to Reveal Inner Container's Hidden Content</a>
  <div class="inner-container">
    <div class="hidden-content">This is hidden content. When triggered by hover, its height transitions from 0 to 100%, which pushes other content in the same container down gradually.</div>
    <div>Within the same container, this other content is pushed down gradually as the hidden content's height transitions from 0 to 100%.</div>
  </div>
</div>

Press the Run Code Snippet button to see the transition in action.
It's CSS only, with no specific height.

夜灵血窟げ 2024-09-15 01:07:26

您可以从 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;
}

You can transition from height:0 to height:auto providing that you also provide min-height and max-height.

div.stretchy{
    transition: 1s linear;
}

div.stretchy.hidden{
    height: 0;
}

div.stretchy.visible{
    height: auto;
    min-height:40px;
    max-height:400px;
}
雨落星ぅ辰 2024-09-15 01:07:26

您可以通过使用剪辑路径创建反向(折叠)动画来做到这一点。

#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>

You could do this by creating a reverse (collapse) animation with clip-path.

#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>

风吹雨成花 2024-09-15 01:07:26

一句话解决方案:使用填充过渡。对于大多数情况(例如手风琴)来说这已经足够了,甚至更好,因为填充值通常不大,所以速度很快。

如果你想让动画过程更好,只需提高padding值即可。

.parent{ border-top: #999 1px solid;}
h1{ margin: .5rem; font-size: 1.3rem}
.children {
  height: 0;
  overflow: hidden;
  background-color: #dedede;
  transition: padding .2s ease-in-out, opacity .2s ease-in-out;
  padding: 0 .5rem;
  opacity: 0;
}
.children::before, .children::after{ content: "";display: block;}
.children::before{ margin-top: -2rem;}
.children::after{ margin-bottom: -2rem;}
.parent:hover .children {
  height: auto;
  opacity: 1;
  padding: 2.5rem .5rem;/* 0.5 + abs(-2), make sure it's less than expected min-height */
}
<div class="parent">
  <h1>Hover me</h1>
  <div class="children">Some content
    <br>Some content
    <br>Some content
    <br>Some content
    <br>Some content
    <br>Some content
    <br>
  </div>
</div>
<div class="parent">
  <h1>Hover me(long content)</h1>
  <div class="children">Some content
    <br>Some content<br>Some content
    <br>Some content<br>Some content
    <br>Some content<br>Some content
    <br>Some content<br>Some content
    <br>Some content<br>Some content
    <br>
  </div>
</div>
<div class="parent">
  <h1>Hover me(short content)</h1>
  <div class="children">Some content
    <br>Some content
    <br>Some content
    <br>
  </div>
</div>

One sentence solution: Use padding transition. It's enough for most of cases such as accordion, and even better because it's fast due to that the padding value is often not big.

If you want the animation process to be better, just raise the padding value.

.parent{ border-top: #999 1px solid;}
h1{ margin: .5rem; font-size: 1.3rem}
.children {
  height: 0;
  overflow: hidden;
  background-color: #dedede;
  transition: padding .2s ease-in-out, opacity .2s ease-in-out;
  padding: 0 .5rem;
  opacity: 0;
}
.children::before, .children::after{ content: "";display: block;}
.children::before{ margin-top: -2rem;}
.children::after{ margin-bottom: -2rem;}
.parent:hover .children {
  height: auto;
  opacity: 1;
  padding: 2.5rem .5rem;/* 0.5 + abs(-2), make sure it's less than expected min-height */
}
<div class="parent">
  <h1>Hover me</h1>
  <div class="children">Some content
    <br>Some content
    <br>Some content
    <br>Some content
    <br>Some content
    <br>Some content
    <br>
  </div>
</div>
<div class="parent">
  <h1>Hover me(long content)</h1>
  <div class="children">Some content
    <br>Some content<br>Some content
    <br>Some content<br>Some content
    <br>Some content<br>Some content
    <br>Some content<br>Some content
    <br>Some content<br>Some content
    <br>
  </div>
</div>
<div class="parent">
  <h1>Hover me(short content)</h1>
  <div class="children">Some content
    <br>Some content
    <br>Some content
    <br>
  </div>
</div>

撧情箌佬 2024-09-15 01:07:26

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

所以li:hover是当鼠标进入状态时,然后非hovered属性上的转换将是鼠标离开。

希望这会有所帮助。

例如:

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

Expanding on @jake's answer, the transition will go all the way to the max height value, causing an extremely fast animation - if you set the transitions for both :hover and off you can then control the crazy speed a little bit more.

So the li:hover is when the mouse enters the state and then the transition on the non-hovered property will be the mouse leave.

Hopefully this will be of some help.

e.g:

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

Here's a fiddle: http://jsfiddle.net/BukwJ/

请爱~陌生人 2024-09-15 01:07:26

我理解这个问题要求不使用 JavaScript 的解决方案。但对于那些感兴趣的人来说,这是我仅使用一点 JS 的解决方案。

好的,所以默认高度会改变的元素的css设置为height: 0;并且打开时height: auto;。它还具有 transition: height .25s escape;。但问题当然是它不会转换为 height: auto; 或从 height: auto; 转换

所以我所做的是在打开或关闭时将高度设置为 scrollHeight元素的属性。这种新的内联样式将具有更高的特异性,并覆盖 height: auto;height: 0; 并运行过渡。

打开时,我添加一个 transitionend 事件监听器,该监听器仅运行一次,然后删除内联样式,将其设置回 height: auto; 这将允许元素在必要时调整大小,正如这个带有子菜单的更复杂的示例 https://codepen.io/ninjabonsai/pen/GzYyVe

关闭时,我会在下一个 事件循环之后立即删除内联样式< /a> 使用 setTimeout 循环,无延迟。这意味着 height: auto; 被暂时覆盖,从而允许转换回 height 0;

const showHideElement = (element, open) => {
  element.style.height = element.scrollHeight + 'px';
  element.classList.toggle('open', open);

  if (open) {
    element.addEventListener('transitionend', () => {
      element.style.removeProperty('height');
    }, {
      once: true
    });
  } else {
    window.setTimeout(() => {
      element.style.removeProperty('height');
    });
  }
}

const menu = document.body.querySelector('#menu');
const list = document.body.querySelector('#menu > ul')

menu.addEventListener('mouseenter', () => showHideElement(list, true));
menu.addEventListener('mouseleave', () => showHideElement(list, false));
#menu > ul {
  height: 0;
  overflow: hidden;
  background-color: #999;
  transition: height .25s ease-out;
}

#menu > ul.open {
  height: auto;
}
<div id="menu">
  <a>hover me</a>
  <ul>
    <li>item</li>
    <li>item</li>
    <li>item</li>
    <li>item</li>
    <li>item</li>
  </ul>
</div>

I understand the question asks for a solution without JavaScript. But for those interested here's my solution using just a little bit of JS.

ok, so the element's css whose height will change by default is set to height: 0; and when open height: auto;. It also has transition: height .25s ease-out;. But of course the problem is that it won't transition to or from height: auto;

So what i've done is when opening or closing set the height to the scrollHeight property of the element. This new inline style will have higher specificity and override both height: auto; and height: 0; and the transition runs.

When opening i add a transitionend event listener which will run just once then remove the inline style setting it back to height: auto; which will allow the element to resize if necessary, as in this more complex example with sub menus https://codepen.io/ninjabonsai/pen/GzYyVe

When closing i remove the inline style right after the next event loop cycle by using setTimeout with no delay. This means height: auto; is temporarily overridden which allows the transition back to height 0;

const showHideElement = (element, open) => {
  element.style.height = element.scrollHeight + 'px';
  element.classList.toggle('open', open);

  if (open) {
    element.addEventListener('transitionend', () => {
      element.style.removeProperty('height');
    }, {
      once: true
    });
  } else {
    window.setTimeout(() => {
      element.style.removeProperty('height');
    });
  }
}

const menu = document.body.querySelector('#menu');
const list = document.body.querySelector('#menu > ul')

menu.addEventListener('mouseenter', () => showHideElement(list, true));
menu.addEventListener('mouseleave', () => showHideElement(list, false));
#menu > ul {
  height: 0;
  overflow: hidden;
  background-color: #999;
  transition: height .25s ease-out;
}

#menu > ul.open {
  height: auto;
}
<div id="menu">
  <a>hover me</a>
  <ul>
    <li>item</li>
    <li>item</li>
    <li>item</li>
    <li>item</li>
    <li>item</li>
  </ul>
</div>

够运 2024-09-15 01:07:26

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

但结果是我们仅使用 CSS 获得高性能转换,并且单一的过渡函数可以平滑地实现过渡;圣杯!

当然,也有一个缺点!我不知道如何控制内容被截断的宽度(overflow:hidden);由于 padding-bottom hack,宽度和高度密切相关。不过可能有办法,所以会回来讨论它。

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>

This solution uses a few techniques:

  • padding-bottom:100% 'hack' where percentages are defined in terms of the current width of the element. More info on this technique.
  • float shrink-wrapping, (necessitating an extra div to apply the float clearing hack)
  • non-semantic use of https://caniuse.com/#feat=css-writing-mode and some transformations to undo it (this allows use of the padding hack above in a vertical context)

The upshot though is that we get performant transitioning using CSS only, and a single transition function to smoothly achieve the transition; the holy grail!

Of course, there's a downside! I can't work out how to control the width at which content gets cut off (overflow:hidden); because of the padding-bottom hack, the width and height are intimately related. There may be a way though, so will come back to it.

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>

绝不放开 2024-09-15 01:07:26

我最近在 li 元素上转换 max-height,而不是包装 ul

原因是,与大 max-heights 相比,小 max-heights 的延迟远不那么明显(如果有的话),而且我还可以设置我的 max-height 值相对于 lifont-size,而不是使用 ems 的任意巨大数字>雷姆斯

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

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

更新:自从我写下这个答案以来,CSS 已经发展到包含更多优雅的技术。我相信今天最好的方法是使用 css grid 和 grid-template-row,如这个答案中所述:
https://stackoverflow.com/a/69802208/3594432

I've recently been transitioning the max-height on the li elements rather than the wrapping ul.

The reasoning is that the delay for small max-heights is far less noticeable (if at all) compared to large max-heights, and I can also set my max-height value relative to the font-size of the li rather than some arbitrary huge number by using ems or rems.

If my font size is 1rem, I'll set my max-height to something like 3rem (to accommodate wrapped text). You can see an example here:

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

Update: CSS has evolved to include much more elegant techniques since I wrote this answer. I believe the best today is by using css grid and grid-template-row as described in this answer:
https://stackoverflow.com/a/69802208/3594432

和我恋爱吧 2024-09-15 01:07:26

每当 DOM 发生更改时,为跟踪元素设置 style="" 属性。 名为mutant-transition的插件 您可以使用CSS进行转换,而不是使用hacks。您不必编写任何 JavaScript。只需包含 JavaScript 库并指定您想要在 HTML 中查看的属性即可。您不必使用固定高度的 CSS。使用 data-mutant-attributes="X" 设置您想要在相关元素上跟踪的内容。

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

这使用 MutationObserver 来跟踪 DOM 中的更改。您无需进行任何设置或使用 JavaScript 来手动制作动画。自动跟踪更改。但是,因为它使用 MutationObserver,所以这只会在 IE11+ 中进行转换。 < IE11 将看到快速更改(无过渡)。

Fiddles

Set the style="" attributes for tracked elements whenever there's a change in the DOM. plugin called mutant-transition You can use CSS for your transitions and not use hacks. You don't have to write any JavaScript. Just include the JavaScript library and specify which attributes you want to watch in the HTML. You don't have to use fixed height CSS. Set what you want to track on the element in question using data-mutant-attributes="X".

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

This uses MutationObserver to follow changes in the DOM. You don't have to set anything up or use JavaScript to manually animate. Changes are tracked automatically. However, because it uses MutationObserver, this will only transition in IE11+. < IE11 will see snap-changes (no transition).

Fiddles

子栖 2024-09-15 01:07:26

具有 line-heightpaddingopacitymargin 的替代纯 CSS 解决方案:

body {
  background-color: linen;
}

main {
  background-color: white;
}

[id^="toggle_"] ~ .content {
  line-height: 0;
  opacity: 0;
  padding: 0 .5rem;
  transition: .2s ease-out;
}

[id^="toggle_"] ~ .content > p {
  margin: 0;
    transition: .2s ease-out;
}

[id^="toggle_"]:checked ~ .content   {
  opacity: 1;
  padding: .5rem;
  line-height: 1.5;
}

[id^="toggle_"]:checked ~ .content p  {
    margin-bottom: .75rem;
}

[id^="toggle_"] + label {
  display: flex;
  justify-content: space-between;
  padding: 0.5em 1em;
  background: lightsteelblue;
  border-bottom: 1px solid gray;
  cursor: pointer;
}

[id^="toggle_"] + label:before {
  content: "Show";
}

[id^="toggle_"]:checked + label:before {
  content: "Hide";
}

[id^="toggle_"] + label:after {
  content: "\25BC";
}

[id^="toggle_"]:checked + label:after {
  content: "\25B2";
}
<main>
    <div>
        <input type="checkbox" id="toggle_1" hidden>
        <label for="toggle_1" hidden></label>
        <div class="content">
            <p>
                Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis dolor neque, commodo quis leo ut, auctor tincidunt mauris. Nunc fringilla tincidunt metus, non gravida lorem condimentum non. Duis ornare purus nisl, at porta arcu eleifend eget. Integer lorem ante, porta vulputate dui ut, blandit tempor tellus. Proin facilisis bibendum diam, sit amet rutrum est feugiat ut. Mauris rhoncus convallis arcu in condimentum. Donec volutpat dui eu mollis vulputate. Nunc commodo lobortis nunc at ultrices. Suspendisse in lobortis diam. Suspendisse eget vestibulum ex.
            </p>
        </div>
    </div>
    <div>
        <input type="checkbox" id="toggle_2" hidden>
        <label for="toggle_2" hidden></label>
        <div class="content">
            <p>
                Maecenas laoreet nunc sit amet nulla ultrices auctor. Vivamus sed nisi vitae nibh condimentum pulvinar eu vel lorem. Sed pretium viverra eros ut facilisis. In ut fringilla magna. Sed a tempor libero. Donec sapien libero, lacinia sed aliquet ut, imperdiet finibus tellus. Nunc tellus lectus, rhoncus in posuere quis, tempus sit amet enim. Morbi et erat ac velit fringilla dignissim. Donec commodo, est id accumsan cursus, diam dui hendrerit nisi, vel hendrerit purus dolor ut risus. Phasellus mattis egestas ipsum sed ullamcorper. In diam ligula, rhoncus vel enim et, imperdiet porta justo. Curabitur vulputate hendrerit nisl, et ultricies diam. Maecenas ac leo a diam cursus ornare nec eu quam.
            </p>
            <p>Sed non vulputate purus, sed consectetur odio. Sed non nibh fringilla, imperdiet odio nec, efficitur ex. Suspendisse ut dignissim enim. Maecenas felis augue, tempor sit amet sem fringilla, accumsan fringilla nibh. Quisque posuere lacus tortor, quis malesuada magna elementum a. Nullam id purus in ante molestie tincidunt. Morbi luctus orci eu egestas dignissim. Sed tincidunt, libero quis scelerisque bibendum, ligula nisi gravida libero, id lacinia nulla leo in elit.
            </p>
            <p>Aenean aliquam risus id consectetur sagittis. Aliquam aliquam nisl eu augue accumsan, vel maximus lorem viverra. Aliquam ipsum dolor, tempor et justo ac, fermentum mattis dui. Etiam at posuere ligula. Vestibulum tortor metus, viverra vitae mi non, laoreet iaculis purus. Praesent vel semper nibh. Curabitur a congue lacus. In et pellentesque lorem. Morbi posuere felis non diam vulputate, non vulputate ex vehicula. Vivamus ultricies, massa id sagittis consequat, sem mauris tincidunt nunc, eu vehicula augue quam ut mauris.
            </p>
        </div>
    </div>
        <div>
        <input type="checkbox" id="toggle_3" hidden>
        <label for="toggle_3" hidden></label>
        <div class="content">
            <p>
                Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis dolor neque, commodo quis leo ut, auctor tincidunt mauris. Nunc fringilla tincidunt metus, non gravida lorem condimentum non. Duis ornare purus nisl, at porta arcu eleifend eget. Integer lorem ante, porta vulputate dui ut, blandit tempor tellus. Proin facilisis bibendum diam, sit amet rutrum est feugiat ut. Mauris rhoncus convallis arcu in condimentum. Donec volutpat dui eu mollis vulputate. Nunc commodo lobortis nunc at ultrices. Suspendisse in lobortis diam. Suspendisse eget vestibulum ex.
            </p>
            <p>Sed non vulputate purus, sed consectetur odio. Sed non nibh fringilla, imperdiet odio nec, efficitur ex. Suspendisse ut dignissim enim. Maecenas felis augue, tempor sit amet sem fringilla, accumsan fringilla nibh. Quisque posuere lacus tortor, quis malesuada magna elementum a. Nullam id purus in ante molestie tincidunt. Morbi luctus orci eu egestas dignissim. Sed tincidunt, libero quis scelerisque bibendum, ligula nisi gravida libero, id lacinia nulla leo in elit.
            </p>
        </div>
    </div>
</main>

Alternate CSS-only solution with line-height, padding, opacity and margin:

body {
  background-color: linen;
}

main {
  background-color: white;
}

[id^="toggle_"] ~ .content {
  line-height: 0;
  opacity: 0;
  padding: 0 .5rem;
  transition: .2s ease-out;
}

[id^="toggle_"] ~ .content > p {
  margin: 0;
    transition: .2s ease-out;
}

[id^="toggle_"]:checked ~ .content   {
  opacity: 1;
  padding: .5rem;
  line-height: 1.5;
}

[id^="toggle_"]:checked ~ .content p  {
    margin-bottom: .75rem;
}

[id^="toggle_"] + label {
  display: flex;
  justify-content: space-between;
  padding: 0.5em 1em;
  background: lightsteelblue;
  border-bottom: 1px solid gray;
  cursor: pointer;
}

[id^="toggle_"] + label:before {
  content: "Show";
}

[id^="toggle_"]:checked + label:before {
  content: "Hide";
}

[id^="toggle_"] + label:after {
  content: "\25BC";
}

[id^="toggle_"]:checked + label:after {
  content: "\25B2";
}
<main>
    <div>
        <input type="checkbox" id="toggle_1" hidden>
        <label for="toggle_1" hidden></label>
        <div class="content">
            <p>
                Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis dolor neque, commodo quis leo ut, auctor tincidunt mauris. Nunc fringilla tincidunt metus, non gravida lorem condimentum non. Duis ornare purus nisl, at porta arcu eleifend eget. Integer lorem ante, porta vulputate dui ut, blandit tempor tellus. Proin facilisis bibendum diam, sit amet rutrum est feugiat ut. Mauris rhoncus convallis arcu in condimentum. Donec volutpat dui eu mollis vulputate. Nunc commodo lobortis nunc at ultrices. Suspendisse in lobortis diam. Suspendisse eget vestibulum ex.
            </p>
        </div>
    </div>
    <div>
        <input type="checkbox" id="toggle_2" hidden>
        <label for="toggle_2" hidden></label>
        <div class="content">
            <p>
                Maecenas laoreet nunc sit amet nulla ultrices auctor. Vivamus sed nisi vitae nibh condimentum pulvinar eu vel lorem. Sed pretium viverra eros ut facilisis. In ut fringilla magna. Sed a tempor libero. Donec sapien libero, lacinia sed aliquet ut, imperdiet finibus tellus. Nunc tellus lectus, rhoncus in posuere quis, tempus sit amet enim. Morbi et erat ac velit fringilla dignissim. Donec commodo, est id accumsan cursus, diam dui hendrerit nisi, vel hendrerit purus dolor ut risus. Phasellus mattis egestas ipsum sed ullamcorper. In diam ligula, rhoncus vel enim et, imperdiet porta justo. Curabitur vulputate hendrerit nisl, et ultricies diam. Maecenas ac leo a diam cursus ornare nec eu quam.
            </p>
            <p>Sed non vulputate purus, sed consectetur odio. Sed non nibh fringilla, imperdiet odio nec, efficitur ex. Suspendisse ut dignissim enim. Maecenas felis augue, tempor sit amet sem fringilla, accumsan fringilla nibh. Quisque posuere lacus tortor, quis malesuada magna elementum a. Nullam id purus in ante molestie tincidunt. Morbi luctus orci eu egestas dignissim. Sed tincidunt, libero quis scelerisque bibendum, ligula nisi gravida libero, id lacinia nulla leo in elit.
            </p>
            <p>Aenean aliquam risus id consectetur sagittis. Aliquam aliquam nisl eu augue accumsan, vel maximus lorem viverra. Aliquam ipsum dolor, tempor et justo ac, fermentum mattis dui. Etiam at posuere ligula. Vestibulum tortor metus, viverra vitae mi non, laoreet iaculis purus. Praesent vel semper nibh. Curabitur a congue lacus. In et pellentesque lorem. Morbi posuere felis non diam vulputate, non vulputate ex vehicula. Vivamus ultricies, massa id sagittis consequat, sem mauris tincidunt nunc, eu vehicula augue quam ut mauris.
            </p>
        </div>
    </div>
        <div>
        <input type="checkbox" id="toggle_3" hidden>
        <label for="toggle_3" hidden></label>
        <div class="content">
            <p>
                Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis dolor neque, commodo quis leo ut, auctor tincidunt mauris. Nunc fringilla tincidunt metus, non gravida lorem condimentum non. Duis ornare purus nisl, at porta arcu eleifend eget. Integer lorem ante, porta vulputate dui ut, blandit tempor tellus. Proin facilisis bibendum diam, sit amet rutrum est feugiat ut. Mauris rhoncus convallis arcu in condimentum. Donec volutpat dui eu mollis vulputate. Nunc commodo lobortis nunc at ultrices. Suspendisse in lobortis diam. Suspendisse eget vestibulum ex.
            </p>
            <p>Sed non vulputate purus, sed consectetur odio. Sed non nibh fringilla, imperdiet odio nec, efficitur ex. Suspendisse ut dignissim enim. Maecenas felis augue, tempor sit amet sem fringilla, accumsan fringilla nibh. Quisque posuere lacus tortor, quis malesuada magna elementum a. Nullam id purus in ante molestie tincidunt. Morbi luctus orci eu egestas dignissim. Sed tincidunt, libero quis scelerisque bibendum, ligula nisi gravida libero, id lacinia nulla leo in elit.
            </p>
        </div>
    </div>
</main>

春庭雪 2024-09-15 01:07:26

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

您还只需获取/设置元素自身的高度:

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

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

I realize this thread is getting old, but it ranks high on certain Google searches so I figure it's worth updating.

You also just get/set the element's own height:

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

You should dump this Javascript immediately after target_box's closing tag in an inline script tag.

碍人泪离人颜 2024-09-15 01:07:26

杰克对最大高度动画的回答很棒,但我发现设置较大的最大高度引起的延迟很烦人。

人们可以将可折叠内容移动到内部 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/duZpT

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

Jake's answer to animate the max-height is great, but I found the delay caused by setting a large max-height annoying.

One could move the collapsable content into an inner div and calculate the max height by getting the height of the inner div (via JQuery it'd be the 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');
});

Here's a jsfiddle link: http://jsfiddle.net/pbatey/duZpT

Here's a jsfiddle with the absolute minimal amount of code required: http://jsfiddle.net/8ncjjxh8/

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文