我如何过渡高度:0; 到高度:auto; 使用CSS?

我正在尝试使用CSS转换向下滑动<ul>

<ul>height: 0;开始height: 0; 。 hover时,高度设置为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> 

在转换中使用max-height ,而不是height 。 并设置max-height的值比你的盒子会得到更大的东西。

看到克里斯·乔丹提供的JSFiddle演示在这里的另一个答案 。

 #menu #list { max-height: 0; transition: max-height 0.15s ease-out; overflow: hidden; background: #d5d5d5; } #menu:hover #list { max-height: 500px; transition: max-height 0.25s ease-in; } 
 <div id="menu"> <a>hover me</a> <ul id="list"> <!-- Create a bunch, or not a bunch, of li's to see the timing. --> <li>item</li> <li>item</li> <li>item</li> <li>item</li> <li>item</li> </ul> </div> 

您应该使用scaleY来代替。

HTML:

 <p>Here (scaleY(1))</p> <ul> <li>Coffee</li> <li>Tea</li> <li>Milk</li> </ul> 

CSS:

 ul { background-color: #eee; transform: scaleY(0); transform-origin: top; transition: transform 0.26s ease; } p:hover ~ ul { transform: scaleY(1); } 

我已经在jsfiddle上提供了上述代码的供应商前缀版本, http://jsfiddle.net/dotnetCarpenter/PhyQc/9/并将您的jsfiddle更改为使用scaleY代替高度,; http://jsfiddle.net/dotnetCarpenter/ 7cnfc / 206 / 。

当所涉及的高度之一为auto ,您不能在高度上进行animation,必须设置两个明确的高度。

你可以用一点点的非语义jiggery-pokery。 我通常的做法是animation化一个外部DIV的高度,这个外部DIV有一个单独的孩子,这个DIV只用于测量内容的高度。

CSS:

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

JS:

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

HTML:

 <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的高度设置为自动,然后生成animation即可,但似乎不起作用(高度已设置,但没有animation发生)。

CSS:

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

JS:

 function growDiv() { var growDiv = document.getElementById('grow'); if (growDiv.clientHeight) { growDiv.style.height = 0; } else { growDiv.style.height = 'auto'; } } 

HTML:

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

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

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

工作示例: https : //jsfiddle.net/vk4dvjuj/

我的解决方法是将max-height转换为精确的内容高度,然后使用transitionEndcallback将max-height设置为9999px,以便内容可以自由resize。

 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> 

使用CSS3转换animation高度的可视化解决方法是animation化填充。

你不完全得到完整的擦除效果,但玩过渡时间和填充值应该让你足够接近。 如果你不想明确地设置高度/最大高度,这应该是你在找什么。

 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之上)

被接受的答案适用于大多数情况,但当div高度变化很大时,效果不佳 – animation速度不取决于内容的实际高度,而且可能看起来不稳定。

您仍然可以使用CSS执行实际的animation,但是您需要使用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> 

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

  • 一开始就没有拖延,转型不会提前结束。 在两个方向(展开和折叠)中,如果在CSS中指定300ms的过渡时间,则过渡需要300ms的时间。
  • 它正在转换实际的高度(与transform: scaleY(0) ),所以如果可折叠元素后面有内容,它就是正确的。
  • 虽然(就像在其他的解决scheme中一样)有幻数(比如“select一个比你的盒子更高的长度”),如果你的假设最终被错误的话,这并不是致命的。 在这种情况下,转换可能看起来并不奇怪,但在转换之前和之后,这不是一个问题:在扩展( height: auto )状态下,整个内容总是具有正确的高度(不同于例如,如果selectmax-height原来是太低)。 而在折叠状态下,高度应该是零。

演示

这里有一个三个可折叠元素的演示,所有不同的高度,都使用相同的CSS。 点击“运行代码段”后,您可能需要点击“完整页面”。 请注意,JavaScript仅切换collapsed CSS类,不涉及测量。 (您可以通过使用checkbox或: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> 

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/

Ok, so I think I came up with a super simple answer… 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> 

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> 

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> 

There was little mention of the Element.scrollHeight property which can be useful here and still may be used with a pure CSS transition. The property always contains the "full" height of an element, regardless of whether and how its content overflows as a result of collapsed height (eg height: 0 ).

As such, for a height: 0 (effectively fully collapsed) element, its "normal" or "full" height is still readily available through its scrollHeight value (invariably a pixel length).

For such an element, assuming it already has the transition set up like eg (using ul as per original question):

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

We can trigger desired animated "expansion" of height, using CSS only, with something like the following (here assuming ul variable refers to the list):

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

而已。 If you need to collapse the list, either of the two following statements will do:

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

My particular use case revolved around animating lists of unknown and often considerable lengths, so I was not comfortable settling on an arbitrary "large enough" height or max-height specification and risking cut-off content or content that you suddenly need to scroll (if overflow: auto , for example). Additionally, the easing and timing is broken with max-height -based solutions, because the used height may reach its maximum value a lot sooner than it would take for max-height to reach 9999px . And as screen resolutions grow, pixel lengths like 9999px leave a bad taste in my mouth. This particular solution solves the problem in an elegant manner, in my opinion.

Finally, here is hoping that future revisions of CSS address authors need to do these kind of things even more elegantly — revisit the notion of "computed" vs "used" and "resolved" values, and consider whether transitions should apply to computed values, including transitions with width and height (which currently get a bit of a special treatment).

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

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://fiddle.jshell.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.

I think I came up with a really solid solution

好! I know this problem is as old as the internet but I think I have a solution which I turned into a plugin called mutant-transition . My solution sets the style="" attributes for tracked elements whenever theres a change in the DOM. the end result is that you can use good ole CSS for your transitions and not use hacky fixes or special javascript. The only thing you have to do is 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 solution uses MutationObserver to follow changes in the DOM. Because of this, you don't really have to set anything up or use javascript to manually animate things. Changes are tracked automatically. However, because it uses MutationObserver, this will only transition in IE11+.

Fiddles!

  • Demonstrating transitioning from height: auto to height: 100%
  • Demonstrating transitioning height: auto when adding children

Here's a way to transition from any starting height, including 0, to auto (full size and flexible) without requiring hard-set code on a per-node basis or any user-code to initialize: https://github.com/csuwildcat/transition-auto . This is basically the holy grail for what you want, I believe –> http://codepen.io/csuwldcat/pen/kwsdF . Just slap the following JS file into your page, and all you need to do after that is add/remove a single boolean attribute – reveal="" – from the nodes you want to expand and contract.

Here's all you need to do as the user, once you include the code block found below the example code:

 /*** Nothing out of the ordinary in your styles ***/ <style> div { height: 0; overflow: hidden; transition: height 1s; } </style> /*** Just add and remove one attribute and transition to/from auto! ***/ <div> I have tons of content and I am 0px in height you can't see me... </div> <div reveal> I have tons of content and I am 0px in height you can't see me... but now that you added the 'reveal' attribute, I magically transitioned to full height!... </div> 

Here's the code block to include in your page, after that, it's all gravy:

Drop this JS file in your page – it all Just Works™

/ * Code for height: auto; transitioning * /

 (function(doc){ /* feature detection for browsers that report different values for scrollHeight when an element's overflow is hidden vs visible (Firefox, IE) */ var test = doc.documentElement.appendChild(doc.createElement('x-reveal-test')); test.innerHTML = '-'; test.style.cssText = 'display: block !important; height: 0px !important; padding: 0px !important; font-size: 0px !important; border-width: 0px !important; line-height: 1px !important; overflow: hidden !important;'; var scroll = test.scrollHeight || 2; doc.documentElement.removeChild(test); var loading = true, numReg = /^([0-9]*\.?[0-9]*)(.*)/, skipFrame = function(fn){ requestAnimationFrame(function(){ requestAnimationFrame(fn); }); }, /* 2 out of 3 uses of this function are purely to work around Chrome's catastrophically busted implementation of auto value CSS transitioning */ revealFrame = function(el, state, height){ el.setAttribute('reveal-transition', 'frame'); el.style.height = height; skipFrame(function(){ el.setAttribute('reveal-transition', state); el.style.height = ''; }); }, transitionend = function(e){ var node = e.target; if (node.hasAttribute('reveal')) { if (node.getAttribute('reveal-transition') == 'running') revealFrame(node, 'complete', ''); } else { node.removeAttribute('reveal-transition'); node.style.height = ''; } }, animationstart = function(e){ var node = e.target, name = e.animationName; if (name == 'reveal' || name == 'unreveal') { if (loading) return revealFrame(node, 'complete', 'auto'); var style = getComputedStyle(node), offset = (Number(style.paddingTop.match(numReg)[1])) + (Number(style.paddingBottom.match(numReg)[1])) + (Number(style.borderTopWidth.match(numReg)[1])) + (Number(style.borderBottomWidth.match(numReg)[1])); if (name == 'reveal'){ node.setAttribute('reveal-transition', 'running'); node.style.height = node.scrollHeight - (offset / scroll) + 'px'; } else { if (node.getAttribute('reveal-transition') == 'running') node.style.height = ''; else revealFrame(node, 'running', node.scrollHeight - offset + 'px'); } } }; doc.addEventListener('animationstart', animationstart, false); doc.addEventListener('MSAnimationStart', animationstart, false); doc.addEventListener('webkitAnimationStart', animationstart, false); doc.addEventListener('transitionend', transitionend, false); doc.addEventListener('MSTransitionEnd', transitionend, false); doc.addEventListener('webkitTransitionEnd', transitionend, false); /* Batshit readyState/DOMContentLoaded code to dance around Webkit/Chrome animation auto-run weirdness on initial page load. If they fixed their code, you could just check for if(doc.readyState != 'complete') in animationstart's if(loading) check */ if (document.readyState == 'complete') { skipFrame(function(){ loading = false; }); } else document.addEventListener('DOMContentLoaded', function(e){ skipFrame(function(){ loading = false; }); }, false); /* Styles that allow for 'reveal' attribute triggers */ var styles = doc.createElement('style'), t = 'transition: none; ', au = 'animation: reveal 0.001s; ', ar = 'animation: unreveal 0.001s; ', clip = ' { from { opacity: 0; } to { opacity: 1; } }', r = 'keyframes reveal' + clip, u = 'keyframes unreveal' + clip; styles.textContent = '[reveal] { -ms-'+ au + '-webkit-'+ au +'-moz-'+ au + au +'}' + '[reveal-transition="frame"] { -ms-' + t + '-webkit-' + t + '-moz-' + t + t + 'height: auto; }' + '[reveal-transition="complete"] { height: auto; }' + '[reveal-transition]:not([reveal]) { -webkit-'+ ar +'-moz-'+ ar + ar +'}' + '@-ms-' + r + '@-webkit-' + r + '@-moz-' + r + r + '@-ms-' + u +'@-webkit-' + u + '@-moz-' + u + u; doc.querySelector('head').appendChild(styles); })(document); 

/ * Code for DEMO * /

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

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/

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.

Flexbox Solution

加:

  • 简单
  • no js
  • smooth transition

Minus:

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

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

 <div id="container"> <div id="child-a"> aksdhakhdklashdas äa jdasd asd das sadasdads sadas dasd asdas d das sadasdads sadas dasd asdas ddas sadasdads sadas dasd asdas ddas sadasdads sadas dasd asdas ddas sadasdads sadas dasd asdas ddas sadasdads sadas dasd </div> <div id="child-b"> </div> </div> #container { display: flex; flex-direction: column; width: 200px; height: 300px; background: wheat; > div { transition: 0.25s; } #child-a { flex-basis: auto; background: #333; color: #777; overflow: hidden; flex-grow: 0; } #child-b { flex-basis: 100%; background: white; flex-shrink: 0; } &:hover { #child-a { flex-shrink: 0; flex-grow: 1; } #child-b { flex-shrink: 1; flex-grow: 0; } } } 

JS小提琴

Here's a solution I just used in combination with jQuery. This works for the following HTML structure:

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

and the function:

  $('#main-nav li ul').each(function(){ $me = $(this); //Count the number of li elements in this UL var liCount = $me.find('li').size(), //Multiply the liCount by the height + the margin on each li ulHeight = liCount * 28; //Store height in the data-height attribute in the UL $me.attr("data-height", ulHeight); }); 

You could then use a click function to set and remove the height using css()

 $('#main-nav li a.main-link').click(function(){ //Collapse all submenus back to 0 $('#main-nav li ul').removeAttr('style'); $(this).parent().addClass('current'); //Set height on current submenu to it's height var $currentUl = $('li.current ul'), currentUlHeight = $currentUl.attr('data-height'); }) 

CSS:

 #main-nav li ul { height: 0; position: relative; overflow: hidden; opacity: 0; filter: alpha(opacity=0); -ms-filter: "alpha(opacity=0)"; -khtml-opacity: 0; -moz-opacity: 0; -webkit-transition: all .6s ease-in-out; -moz-transition: all .6s ease-in-out; -o-transition: all .6s ease-in-out; -ms-transition: all .6s ease-in-out; transition: all .6s ease-in-out; } #main-nav li.current ul { opacity: 1.0; filter: alpha(opacity=100); -ms-filter: "alpha(opacity=100)"; -khtml-opacity: 1.0; -moz-opacity: 1.0; } .ie #main-nav li.current ul { height: auto !important } #main-nav li { height: 25px; display: block; margin-bottom: 3px } 

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.

例如:

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

I was able to do this. I have a .child & a .parent div. The child div fits perfectly within the parent's width/height with absolute positioning. I then animate the translate property to push it's Y value down 100% . Its very smooth animation, no glitches or down sides like any other solution here.

Something like this, pseudo code

 .parent{ position:relative; overflow:hidden; } /** shown state */ .child { position:absolute;top:0;:left:0;right:0;bottom:0; height: 100%; transition: transform @overlay-animation-duration ease-in-out; .translate(0, 0); } /** Animate to hidden by sliding down: */ .child.slidedown { .translate(0, 100%); /** Translate the element "out" the bottom of it's .scene container "mask" so its hidden */ } 

You would specify a height on .parent , in px , % , or leave as auto . This div then masks out the .child div when it slides down.

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

I have not read everything in detail but I have had this problem recently and I did what follows:

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

This allows you to have a div that at first shows content up to 200px height and on hover it's size becomes at least as high as the whole content of the div. The Div does not become 3000px but 3000px is the limit that I am imposing. Make sure to have the transition on the non :hover, otherwise you might get some strange rendering. In this way the :hover inherits from the non :hover.

Transition does not work form px to % or to auto. You need to use same unit of measure. 这对我来说很好。 Using HTML5 makes it perfect….

Remember that there is always a work around… ; )

Hope someone finds this useful

I believe the height:auto/max-height solution will only work if you're expanding area is greater than the height you want to restrict.

If you have a max-height of 300px , but a combo box dropdown, which can return 50px , then max-height won't help you, 50px is variable depending on the number of elements, you can arrive to an impossible situation where I can't fix it because the height is not fixed, height:auto was the solution, but I can't use transitions with this.

Set the height to auto and transition the max-height.

Tested on Chrome v17

 div { position: absolute; width:100%; bottom:0px; left:0px; background:#333; color: #FFF; max-height:100%; /**/ height:auto; /**/ -webkit-transition: all 0.2s ease-in-out; -moz-transition: all 0.2s ease-in-out; -o-transition: all 0.2s ease-in-out; -ms-transition: all 0.2s ease-in-out; transition: all 0.2s ease-in-out; } .close { max-height:0%; /**/ } 

The max-height solution from Jake works well, if the hard-coded max-height value supplied is not much bigger than the real height (because otherwise there are undesirable delays and timing problems). On the other hand if the hard-coded value accidentially is not bigger than the real height the element won't open up completely.

The following CSS only solution also requires a hard-coded size that should be bigger than most of the occurring real sizes. However this solution also works if the real size is in some situations bigger than the hard-coded size. In that event the transition might jump a bit, but it will never leave a partially visible element. So this solution could also be used for unknown content, eg from a database, where you just know that the content is usually not bigger than x pixels, but there are exceptions.

Idea is to use a negative value for margin-bottom (or margin-top for a slightly diffenrent animation) and to place the content element into a middle element with overflow:hidden. The negative margin of the content element so reduces the height of the middle element.

The following code uses a transition on margin-bottom from -150px to 0px. This alone works fine as long as the content element is not higher than 150px. In addition it uses a transition on max-height for the middle element from 0px to 100%. This finally hides the middle element if the content element is higher than 150px. For max-height the transition is just used to delay its application by a second when closing, not for a smooth visiual effect ( and therefore it can run from 0px to 100%).

CSS:

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

HTML:

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

The value for margin bottom should be negative and as close as possible to the real height of the content element. If it('s absoute value) is bigger there are similar delay and timing problems as with the max-height solutions, which however can be limited as long as the hard coded size is not much bigger than the real one. If the absolute value for margin-bottom is smaller than the real height the tansition jumps a bit. In any case after the transition the content element is either fully displayed or fully removed.

For more details see my blog post http://www.taccgl.org/blog/css_transition_display.html#combined_height

This isn't exactly a "solution" to the problem, but more of a workaround. It only works as written with text, but can be changed to work with other elements as needed I'm sure.

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

Here is an example: http://codepen.io/overthemike/pen/wzjRKa

Essentially, you set the font-size to 0 and transition that instead of the height, or max-height, or scaleY() etc. at a quick enough pace to get the height to transform to what you want. To transform the actual height with CSS to auto isn't currently possible, but transforming the content within is, hence the font-size transition.

  • Note – there IS javascript in the codepen, but it's only purpose is to add/remove css classes on click for the accordion. This can be done with hidden radio buttons, but I wasn't focused on that, just the height transformation.