如何过渡高度:0;高度:自动;使用CSS?

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

<ul>height: 0;开始。在悬停时,高度设置为height:auto;。然而,这导致它简单地出现,没有过渡,

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

如果不使用JavaScript,我还能怎么做?

#child0 {height: 0;overflow: hidden;background-color: #dedede;-moz-transition: height 1s ease;-webkit-transition: height 1s ease;-o-transition: height 1s ease;transition: height 1s ease;}#parent0:hover #child0 {height: auto;}#child40 {height: 40px;overflow: hidden;background-color: #dedede;-moz-transition: height 1s ease;-webkit-transition: height 1s ease;-o-transition: height 1s ease;transition: height 1s ease;}#parent40:hover #child40 {height: auto;}h1 {font-weight: bold;}
The only difference between the two snippets of CSS is one has height: 0, the other height: 40.<hr><div id="parent0"><h1>Hover me (height: 0)</h1><div id="child0">Some content<br>Some content<br>Some content<br>Some content<br>Some content<br>Some content<br></div></div><hr><div id="parent40"><h1>Hover me (height: 40)</h1><div id="child40">Some content<br>Some content<br>Some content<br>Some content<br>Some content<br>Some content<br></div></div>

1576732 次浏览

您可以使用一些非语义的jiggery-pokery。我通常的方法是为具有单个子级的外部DIV的高度设置动画,这是一个仅用于测量内容高度的无样式DIV。

function growDiv() {var growDiv = document.getElementById('grow');if (growDiv.clientHeight) {growDiv.style.height = 0;} else {var wrapper = document.querySelector('.measuringWrapper');growDiv.style.height = wrapper.clientHeight + "px";}}
#grow {-moz-transition: height .5s;-ms-transition: height .5s;-o-transition: height .5s;-webkit-transition: height .5s;transition: height .5s;height: 0;overflow: hidden;outline: 1px solid red;}
<input type="button" onclick="growDiv()" value="grow"><div id='grow'><div class='measuringWrapper'><div>The contents of my div.</div><div>The contents of my div.</div><div>The contents of my div.</div><div>The contents of my div.</div><div>The contents of my div.</div><div>The contents of my div.</div></div></div>

人们希望能够省略.measuringWrapper并将DIV的高度设置为自动并具有动画,但这似乎不起作用(高度设置,但没有动画发生)。

function growDiv() {var growDiv = document.getElementById('grow');if (growDiv.clientHeight) {growDiv.style.height = 0;} else {growDiv.style.height = 'auto';}}
#grow {-moz-transition: height .5s;-ms-transition: height .5s;-o-transition: height .5s;-webkit-transition: height .5s;transition: height .5s;height: 0;overflow: hidden;outline: 1px solid red;}
<input type="button" onclick="growDiv()" value="grow"><div id='grow'><div>The contents of my div.</div><div>The contents of my div.</div><div>The contents of my div.</div><div>The contents of my div.</div><div>The contents of my div.</div><div>The contents of my div.</div></div>

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

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

您可以从高度:0过渡到高度:自动,前提是您还提供min-高度和max-高度。

div.stretchy{transition: 1s linear;}
div.stretchy.hidden{height: 0;}
div.stretchy.visible{height: auto;min-height:40px;max-height:400px;}

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

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

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

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

使用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/(在jsFiddle上重复使用stephband)

这是我刚刚与jQuery结合使用的解决方案。这适用于以下超文本标记语言结构:

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

和功能:

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

然后,您可以使用单击函数使用css()设置和删除高度

$('#main-nav li a.main-link').click(function(){//Collapse all submenus back to 0$('#main-nav li ul').removeAttr('style');
$(this).parent().addClass('current');
//Set height on current submenu to it's heightvar $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 }

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

请参阅Chris Jordan在另一个回答中提供的jsfiddle演示

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

将高度设置为自动并转换最大高度。

测试Chromev17

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%; /**/}

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

var content = $('#content');content.inner = $('#content .inner'); // inner div needed to get size of content when closed
// css transition callbackcontent.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 heightcontent.removeClass('transitions').css('max-height', content.contentHeight);setTimeout(function(){            
// enable & start transitioncontent.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 heightcontent.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>

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

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

这允许您有一个div,首先显示高达200px高度的内容,在悬停时,它的大小至少与div的整个内容一样高。Div不会变成3000px,但3000px是我强加的限制。确保在非:hover上有过渡,否则您可能会得到一些奇怪的渲染。通过这种方式,: hover继承了非:hover。

从px到%或自动转换不起作用。您需要使用相同的测量单位。

你应该改用caleY。

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更改为使用caleY而不是高度。

编辑有些人不喜欢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>

扩展@jake的答案,过渡将一直到最大高度值,从而产生极快的动画-如果您为两者设置过渡:悬停和关闭,那么您可以控制疯狂的速度多一点。

因此,li: hover是当鼠标进入状态时,然后在非悬停属性上的转换将是鼠标离开。

希望这将是一些帮助。

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

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

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

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

编辑:向下滚动查看更新的答案
我正在制作一个下拉列表,看到这篇文章……许多不同的答案,但我决定分享我的下拉列表,太……它并不完美,但至少它将只使用css下拉!我一直在使用转换: 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;}

而hover是:

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

因为ul高度设置为内容,它可以超越你的身体内容,这就是为什么我为ul做了这个:

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

和hover:

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

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

编辑:我只是不敢相信ppl真的使用这个原型!这个下拉菜单只适用于一个子菜单,仅此而已!!我已经更新了一个更好的一个,可以有两个子菜单的LTR和RTL方向与IE 8的支持。LTR的小提琴
RTL小提琴
希望将来有人能找到有用的东西。

这是一种从任何起始高度(包括0)过渡到自动(全尺寸且灵活)的方法,而无需在每个节点的基础上硬设置代码或任何用户代码来初始化:

以下是您作为用户需要做的所有事情,一旦您包含在示例代码下方找到的代码块:

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

以下是要包含在您的页面中的代码块,之后,一切都是肉汁:

将此JS文件放入您的页面-这一切都很有效™

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

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

/*演示代码*/

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

我能够做到这一点。我有一个.child和一个.parent div。子div完美地适合父级的宽度/高度和absolute定位。然后我对translate属性进行动画处理,将其Y值向下推100%。它的动画非常平滑,没有故障或像这里的任何其他解决方案一样向下。

像这样的伪代码

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

您可以在.parentpx%中指定height,或者保留为auto。然后,此div在向下滑动时掩盖了.child div。

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

原因是,与大的max-heights相比,小max-heights的延迟不太明显(如果有的话),我也可以设置我的max-height值相对于lifont-size,而不是使用emsrems的任意巨大数字。

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

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

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

以下仅限CSS的解决方案还需要硬编码的大小应该比大多数实际尺寸都要大。然而如果实际大小在某些情况下大于,则解决方案也有效硬编码的大小。在这种情况下,过渡可能会跳一点,但它永远不会留下部分可见的元素。因此,该解决方案也可用于未知内容,例如来自一个数据库,你只知道内容通常不会更大比x像素,但也有例外。

想法是使用一个负值为边缘底部(或边缘顶部为稍微不同的动画)并将内容元素放入带有溢出的中间元素:隐藏。内容的负边距这样就减少了中间元素的高度。

下面的代码使用了从-150px到-150px的底边距转换0px。只要内容元素不是高于150px。此外,它使用最大高度的过渡中间元素从0px到100%。这最终隐藏了中间元素如果内容元素高于150px。最大高度的过渡只是用来延迟其应用程序关闭时一秒钟,不是为了平滑的视觉效果(因此它可以从0px运行到100%)。

css:

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

超文本标记语言:

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

边距底部的值应该是负值,并且尽可能接近可能为内容元素的实际高度。如果它('s absoute值)更大,有类似的延迟和定时问题最大高度的解决方案,但可以是有限的,只要硬编码的大小并不比真实的大多少。如果绝对边距底部的值小于实际高度过渡跳了一点。在过渡之后的任何情况下内容元素要么完全显示,要么完全删除。

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

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

使用max-height为每个状态提供不同的转换缓解和延迟。

超文本标记语言:

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

我想我找到了一个可靠的解决方案

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

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

就是这样!此解决方案使用突变观察器来跟踪DOM中的更改。因此,您实际上不必设置任何内容或使用javascript手动制作动画。更改会自动跟踪。但是,因为它使用MutationObserv,这只会在IE11+中转换。

小提琴!

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

好的,所以我想我想出了一个超级简单的答案…否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; }

超文本标记语言

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

没有硬编码值。

没有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 unpredictablecontent 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 unpredictablecontent unpredictable content unpredictable content unpredictable content</div></div>

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

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

举个例子:http://codepen.io/overthemike/pen/wzjRKa

本质上,你可以将font-size设置为0,并以足够快的速度转换它,而不是高度、max-高度或caleY()等,以使高度转换为你想要的高度。使用CSS将实际高度转换为自动目前是不可能的,但是转换其中的内容是可能的,因此是font-size转换。

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

当我发布这篇文章时,已经有30多个答案了,但我觉得我的答案比杰克已经接受的答案的答案有所提高。

我不满足于简单地使用max-height和CSS3转换所产生的问题,因为正如许多评论者所指出的那样,您必须将max-height值设置得非常接近实际高度,否则您会得到延迟。

为了解决这个问题(虽然仍然没有使用JavaScript),我添加了另一个超文本标记语言元素来转换transform: translateY CSS值。

这意味着max-heighttranslateY都被使用:max-height允许元素向下推它下面的元素,而translateY给出了我们想要的“即时”效果。max-height的问题仍然存在,但它的效果减轻了。这意味着你可以为你的max-height值设置一个更大的高度,而不用担心它。

整体的好处是,在转换回(崩溃)时,用户会立即看到translateY动画,所以max-height需要多长时间并不重要。

作为小提琴的解决方案

body {font-family: sans-serif;}
.toggle {position: relative;border: 2px solid #333;border-radius: 3px;margin: 5px;width: 200px;}
.toggle-header {margin: 0;padding: 10px;background-color: #333;color: white;text-align: center;cursor: pointer;}
.toggle-height {background-color: tomato;overflow: hidden;transition: max-height .6s ease;max-height: 0;}
.toggle:hover .toggle-height {max-height: 1000px;}
.toggle-transform {padding: 5px;color: white;transition: transform .4s ease;transform: translateY(-100%);}
.toggle:hover .toggle-transform {transform: translateY(0);}
<div class="toggle"><div class="toggle-header">Toggle!</div><div class="toggle-height"><div class="toggle-transform"><p>Content!</p><p>Content!</p><p>Content!</p><p>Content!</p></div></div></div>
<div class="toggle"><div class="toggle-header">Toggle!</div><div class="toggle-height"><div class="toggle-transform"><p>Content!</p><p>Content!</p><p>Content!</p><p>Content!</p></div></div></div>

似乎没有合适的解决方案。max-height方法非常好,但在隐藏阶段效果不佳-除非您知道内容的高度,否则会有明显的延迟。

我认为最好的方法是使用max-height而不是只在表演阶段。并且不要在隐藏上使用任何动画。在大多数情况下,它不应该是至关重要的。

max-height应该设置为一个相当大的值,以确保任何内容都适合。动画速度可以使用transition持续时间(speed = max-height / duration)来控制。速度不取决于内容的大小。显示整个内容所需的时间将取决于其大小。

document.querySelector("button").addEventListener("click",function(){document.querySelector("div").classList.toggle("hide");})
div {max-height: 20000px;transition: max-height 3000ms;overflow-y: hidden;}
.hide {max-height: 0;transition: none;}
<button>Toggle</button><div class="hide">Lorem ipsum dolor sit amet, ius solet dignissim honestatis ad. Mea quem tibique intellegat te. Insolens deterruisset cum ea. Te omnes percipit consulatu eos. Vix novum primis salutatus no, eam denique sensibus et, his ipsum senserit ne. Lorem ipsum dolor sit amet, ius solet dignissim honestatis ad. Mea quem tibique intellegat te. Insolens deterruisset cum ea. Te omnes percipit consulatu eos. Vix novum primis salutatus no, eam denique sensibus et, his ipsum senserit ne. Lorem ipsum dolor sit amet, ius solet dignissim honestatis ad. Mea quem tibique intellegat te. Insolens deterruisset cum ea. Te omnes percipit consulatu eos. Vix novum primis salutatus no, eam denique sensibus et, his ipsum senserit ne. Lorem ipsum dolor sit amet, ius solet dignissim honestatis ad. Mea quem tibique intellegat te. Insolens deterruisset cum ea. Te omnes percipit consulatu eos. Vix novum primis salutatus no, eam denique sensibus et, his ipsum senserit ne. Lorem ipsum dolor sit amet, ius solet dignissim honestatis ad. Mea quem tibique intellegat te. Insolens deterruisset cum ea. Te omnes percipit consulatu eos. Vix novum primis salutatus no, eam denique sensibus et, his ipsum senserit ne. Lorem ipsum dolor sit amet, ius solet dignissim honestatis ad. Mea quem tibique intellegat te. Insolens deterruisset cum ea. Te omnes percipit consulatu eos. Vix novum primis salutatus no, eam denique sensibus et, his ipsum senserit ne. Lorem ipsum dolor sit amet, ius solet dignissim honestatis ad. Mea quem tibique intellegat te. Insolens deterruisset cum ea. Te omnes percipit consulatu eos. Vix novum primis salutatus no, eam denique sensibus et, his ipsum senserit ne. Lorem ipsum dolor sit amet, ius solet dignissim honestatis ad. Mea quem tibique intellegat te. Insolens deterruisset cum ea. Te omnes percipit consulatu eos. Vix novum primis salutatus no, eam denique sensibus et, his ipsum senserit ne. Lorem ipsum dolor sit amet, ius solet dignissim honestatis ad. Mea quem tibique intellegat te. Insolens deterruisset cum ea. Te omnes percipit consulatu eos. Vix novum primis salutatus no, eam denique sensibus et, his ipsum senserit ne.</div>

这是一个仅限CSS解决方案,具有以下属性:

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

Demo

这是一个包含三个可折叠元素的演示,它们都使用相同的CSS,高度不同。您可能希望在单击“运行代码段”后单击“整页”。请注意,JavaScript仅切换collapsed CSS类,不涉及测量。(您可以使用复选框或:target在没有任何JavaScript的情况下进行此确切的演示)。还要注意,负责转换的CSS部分非常短,并且超文本标记语言只需要一个额外的包装器元素。

$(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 belowis 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像素高,而你的过渡是300ms长,那么可见过渡在大约150ms之后就已经结束了(或者,在相反的方向上,延迟150ms开始)。

修复第二个问题是第二个转换的地方,这个转换在概念上针对包装器的最低高度(“概念上”,因为我们实际上并没有为此使用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转换为0。这与min-height具有相同的效果:它不会让容器收缩到伪元素当前的任何高度以下。但是因为我们使用的是height,而不是min-height,所以我们现在可以使用max-height(再次延迟应用)在转换结束后将伪元素的实际高度设置为零,以确保至少在转换之外,即使是小元素也有正确的高度。因为min-height::after3而不是max-height,如果我们使用容器的min-height而不是伪元素的height,这将不起作用。就像上一段中的max-height一样,这个max-height也需要一个值作为过渡的另一端。但在这种情况下,我们可以选择50px。

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

flexbox解决方案

优点:

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

缺点:

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

它的工作方式是始终在具有内容的元素上使用flex-base: auto,并转换flex-增长和flex-收缩。

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

* {margin: 0;padding: 0;box-sizing: border-box;transition: 0.25s;font-family: monospace;}
body {margin: 10px 0 0 10px;}
.box {width: 150px;height: 150px;margin: 0 2px 10px 0;background: #2d333b;border: solid 10px #20262e;overflow: hidden;display: inline-flex;flex-direction: column;}
.space {flex-basis: 100%;flex-grow: 1;flex-shrink: 0;}
p {flex-basis: auto;flex-grow: 0;flex-shrink: 1;background: #20262e;padding: 10px;width: 100%;text-align: left;color: white;}
.box:hover .space {flex-grow: 0;flex-shrink: 1;}  
.box:hover p {flex-grow: 1;flex-shrink: 0;}
<div class="box"><div class="space"></div><p>Super Metroid Prime Fusion</p></div><div class="box"><div class="space"></div><p>Resident Evil 2 Remake</p></div><div class="box"><div class="space"></div><p>Yolo The Game</p></div><div class="box"><div class="space"></div><p>Final Fantasy 7 Remake + All Additional DLC + Golden Tophat</p></div><div class="box"><div class="space"></div><p>DerpVille</p></div>

JS小提琴

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

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

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

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

关闭时,我在下一个事件循环周期之后立即通过使用无延迟的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>

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

但结果是我们仅使用CSS获得高性能过渡,并且使用单个过渡函数来顺利实现过渡;圣杯!

当然,这也有缺点!我不知道如何控制内容被切断的宽度(overflow:hidden);因为padding-底黑客,宽度和高度是密切相关的。不过可能有办法,所以会回来的。

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>

您可以通过使用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>

我只是动画<li>元素而不是整个容器:

<style>.menu {border: solid;}.menu ul li {height: 0px;transition: height 0.3s;overflow: hidden;}button:hover ~ .wrapper .menu ul li,button:focus ~ .wrapper .menu ul li,.menu:hover ul li {height: 20px;}</style>

<button>Button</button><div class="wrapper"><div class="menu"><ul><li>menuitem</li><li>menuitem</li><li>menuitem</li><li>menuitem</li><li>menuitem</li><li>menuitem</li></ul></div></div>

您可以添加ul: margin 0;以获得0高度。

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

如果您希望动画过程更好,只需提高填充值。

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

我结合了最大高度和负边距来实现这个动画。

我使用了max-高度:2000px,但如果需要,您可以将该数字推到更高的值。我在展开时动画最大高度和折叠时的边距。

js部分只是点击,可以替换为:hover或复选框为纯css解决方案。

到目前为止我只看到两个问题,

  1. 转换定时是有限的。(我只添加了2个定时)
  2. 如果您在下拉列表折叠时再次单击,它将跳转。

这是结果

[...document.querySelectorAll('.ab')].forEach(wrapper => {wrapper.addEventListener('click', function () {this.classList.toggle('active');});});
* {margin: 0;box-sizing: border-box;}
.c {overflow: hidden;}
.items {width: 100%;visibility: hidden;max-height: 0;margin-bottom: -2000px;-webkit-transition: margin 0.6s cubic-bezier(1, 0, 1, 1), max-height 0s 0.6s linear, visibility 0s 0.6s linear;transition: margin 0.6s cubic-bezier(1, 0, 1, 1), max-height 0s 0.6s linear, visibility 0s 0.6s linear;}.items > * {padding: 1rem;background-color: #ddd;-webkit-transition: background-color 0.6s ease;transition: background-color 0.6s ease;}.items > *:hover {background-color: #eee;}
.ab {padding: 1rem;cursor: pointer;background: #eee;}.ab.active + .c .items {max-height: 2000px;margin-bottom: 0;visibility: visible;-webkit-transition: max-height 0.6s cubic-bezier(1, 0, 1, 1);transition: max-height 0.6s cubic-bezier(1, 0, 1, 1);}
.dropdown {margin-right: 1rem;}
.wrapper {display: -webkit-box;display: flex;}
<div class="wrapper"><div class="dropdown"><div class="ab">just text</div><div class="ab">just text</div><div class="ab">dropdown</div><div class="c"><div class="items"><p>items</p><p>items</p><p>items asl;dk l;kasl;d sa;lk</p><p>items sal;kd</p><p>items</p></div></div><div class="ab">just text</div><div class="ab">just text</div></div>    
<div class="dropdown"><div class="ab">dropdown</div><div class="c"><div class="items"><p>items</p><p>items</p><p>items</p><p>items</p><p>items</p><p>items</p><p>items</p><p>items</p><p>items</p><p>items</p><p>items</p></div></div><div class="ab">text</div></div>    
<div class="dropdown"><div class="ab">placeholder</div><div class="ab">dropdown</div><div class="c"><div class="items"><p>items</p><p>items</p></div></div><div class="ab">placeholder</div><div class="ab">placeholder</div><div class="ab">placeholder</div></div></div><h1>text to be pushed</h1>

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>

我使用的方法是基于font-size(以em为单位)或至少影响我们要打开的盒子的垂直大小的所有内容。然后,如果有任何东西不在em单位中(例如1px边框),我将其设置为0,当盒子关闭时,通过以*为目标的所有儿童。

动画显示的是字体大小(以%表示,从0到100)。字体大小是什么?父级的已知字体大小。

它工作的规则是动画框中的所有内容必须:

  • 对所有字体大小和行高度使用%
  • 将em用于所有垂直(如高度,边距和填充顶部/底部等)
  • 如果使用px垂直(例如边框顶部/底部),它应该在盒子关闭时被覆盖(例如border-top:0;border-bottom:0;

在某种程度上,您仍然可以使用像素作为参考单位,只需将包装器font-size设置为100px,例如#page{font-size:100px;},因此如果您想要10px内部的任何位置,您都可以使用0.1em

这不是任何人都能写的最漂亮的东西,但是嘿,这些浏览器没有给我们任何漂亮的解决方案来解决这个问题。一旦盒子的高度不可预测,我们别无选择,只能变得有点脏,这是我想出的最不脏的东西。

悬停版本:

https://jsfiddle.net/xpsfkb07/1/

#page {font-size: calc( (35vw + 65vh) / 30); /* just some responsive design as a bonus */}#slidebox {background-color: #e8e8e8;visibility: hidden;font-size: 0; /* animated from 0 to 100% */opacity: 0; /* optional */transition: 0.5s;}a#ahover:hover ~ #slidebox {visibility: visible;font-size: 100%; /* animated from 0 to 100% */opacity: 1; /* optional */}a#ahover:not(:hover) ~ #slidebox * {border-top: 0;border-bottom: 0;/* Put here anything vertical that uses px as unit, in this case the borders */}a#button {display: inline-flex;align-items: center;justify-content: center;position: relative;width: 20em;height: 3em;padding: 0.5em;border: 2px solid #0080ff;border-radius: 0.4em;background-color: #8fbfef;color: #404040;box-sizing: border-box;}#someform {margin-top: 1em;margin-bottom: 1em;padding: 1em 4em 1em 4em;background-color: #d8ffd8;border: 1px solid #888888;}#someform input {display: inline-block;box-sizing: border-box;font-size: 125%;padding: 0.5em;width: 50%; /* anything horizontal can still be in px or % */border-radius: 0.4em;border: 1px solid red;}
<div id=page><a id=ahover href="#">Hover me</a><br>Here is the box that slides:<div id=slidebox>I am the content of the slide box (line1).<br>I am the content of the slide box (line2).<br><a id=button href="#">I am some button in the slide box</a><br>I am the content of the slide box (line3).<br>I am the content of the slide box (line4).<div id=someform>Some box with a form or anything...<br><input type=text value="Text Box"></div>I am the content of the slide box (line5).<br>I am the content of the slide box (line6).</div>And this is after the box.</div>

类更改版本:

https://jsfiddle.net/8xzsrfh6/

const switch_ele = document.getElementById('aclass');switch_ele.addEventListener('click', function(){const box_ele = document.getElementById('slidebox');box_ele.className = box_ele.className == 'show' ? 'hide' : 'show';}, true);
#page {font-size: calc( (35vw + 65vh) / 30); /* just some responsive design as a bonus */}#slidebox {background-color: #e8e8e8;visibility: hidden;font-size: 0; /* animated from 0 to 100% */opacity: 0; /* optional */transition: .5s;}#slidebox.show {visibility: visible;font-size: 100%; /* animated from 0 to 100% */opacity: 1; /* optional */}#slidebox.hide * {border-top: 0;border-bottom: 0;/* Put here anything vertical that uses px as unit, in this case the borders */}a#button {display: inline-flex;align-items: center;justify-content: center;position: relative;width: 20em;height: 3em;padding: 0.5em;border: 2px solid #0080ff;border-radius: 0.4em;background-color: #8fbfef;color: #404040;box-sizing: border-box;}#someform {margin-top: 1em;margin-bottom: 1em;padding: 1em 4em 1em 4em;background-color: #d8ffd8;border: 1px solid #888888;}#someform input {display: inline-block;box-sizing: border-box;font-size: 125%;padding: 0.5em;width: 50%; /* anything horizontal can still be in px or % */border-radius: 0.4em;border: 1px solid red;}
<div id=page><a id=aclass href="#">Switch class w/ js</a><br>Here is the box that slides:<div id=slidebox class=hide>I am the content of the slide box (line1).<br>I am the content of the slide box (line2).<br><a id=button href="#">I am some button in the slide box</a><br>I am the content of the slide box (line3).<br>I am the content of the slide box (line4).<div id=someform>Some box with a form or anything...<br><input type=text value="Text Box"></div>I am the content of the slide box (line5).<br>I am the content of the slide box (line6).</div>And this is after the box.</div>

很多答案,有些比其他更好,大多数使用JS。我相信我在两个易于理解的用例中找到了这一点,尽管其中一个目前仅适用于Firefox。

如果您想切换覆盖,这适用于每个浏览器:

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

这(目前)仅适用于Firefox中的文档流切换:

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

我写了一篇博客文章关于这些技术,链接到需要修复的Chromiumbug100%可用。

根据MDN Web文档auto值被故意排除在CSS转换规范之外,因此在网格和柔性布局中使用height: 100%topflex属性而不是height: auto

展开/折叠覆盖层

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

在文档流中展开/折叠

html {display: grid;}
body {display: flex;flex-direction: column;}
.content {background: aqua;flex-basis: 0;overflow: hidden;transition: 1s;}
span:hover + .content {flex: 1;}
<span>Hover over me!</span>
<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>
<p>Rest of the page content...</p>

我想添加一个关于如何扩展/折叠保持文档流的示例,这个示例适用于使用尾翼css的React应用程序

export function Collapse({ collapsed = true, children }) {return (<div className="grid"><div className="flex flex-col"><div className={`transition-all duration-500 overflow-hidden ${collapsed ? 'basis-0' : 'flex-1'}`}>{children}</div></div></div>);}

深入阅读:https://stackoverflow.com/a/69871346/9226510

灵活高度仅限CSS解决方案

我偶然发现了一个使用flex行为的古怪解决方案。它至少适用于Chrome和Firefox。

  • 首先,高度转换只在0到100%之间起作用,2数值。由于“自动”不是数值,小数0和“自动”之间不存在增量。100%是灵活的值,因此不需要特定的高度。

  • 其次,隐藏内容的外部容器和内部容器都必须设置为show: flex和flex-方向:列。

  • 第三,外部容器必须具有高度属性。高度是多少并不重要,因为flex行为优先于高度,所以我使用高度0。

.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,没有特定的高度。