提问人:Hailwood 提问时间:8/18/2010 最后编辑:TylerHHailwood 更新时间:9/28/2023 访问量:1740196
如何过渡高度:0;到高度:自动;使用CSS?
How can I transition height: 0; to height: auto; using 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>
答:
你可以,用一点非语义的抖动扑克。我通常的方法是对具有单个子项的外部 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>
人们希望能够省去 DIV 的高度,只需将 DIV 的高度设置为自动并使其动画化,但这似乎不起作用(设置了高度,但没有动画发生)。.measuringWrapper
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
评论
folder.style.transition = `height ${maxHeight}ms`;
当前无法在高度上制作动画,当涉及的高度之一是 时,您必须设置两个显式高度。auto
您可以从 height:0 转换为 height:auto,前提是您还提供 min-height 和 max-height。
div.stretchy{
transition: 1s linear;
}
div.stretchy.hidden{
height: 0;
}
div.stretchy.visible{
height: auto;
min-height:40px;
max-height:400px;
}
评论
height
max-height
height
0
auto
max-height
我意识到这个线程已经过时了,但它在某些 Google 搜索中排名很高,所以我认为它值得更新。
您也可以获取/设置元素自身的高度:
var load_height = document.getElementById('target_box').clientHeight;
document.getElementById('target_box').style.height = load_height + 'px';
您应该在内联脚本标记中target_box结束标记之后立即转储此 Javascript。
使用 CSS3 过渡对高度进行动画处理的视觉解决方法是改为对填充进行动画处理。
你没有得到完全的擦除效果,但玩弄过渡持续时间和填充值应该会让你足够接近。如果您不想显式设置 height/max-height,这应该是您要查找的内容。
div {
height: 0;
overflow: hidden;
padding: 0 18px;
-webkit-transition: all .5s ease;
-moz-transition: all .5s ease;
transition: all .5s ease;
}
div.animated {
height: auto;
padding: 24px 18px;
}
http://jsfiddle.net/catharsis/n5XfG/17/(摘自 stephband 上面的 jsFiddle)
评论
这是我刚刚与jQuery结合使用的解决方案。这适用于以下 HTML 结构:
<nav id="main-nav">
<ul>
<li>
<a class="main-link" href="yourlink.html">Link</a>
<ul>
<li><a href="yourlink.html">Sub Link</a></li>
</ul>
</li>
</ul>
</nav>
和功能:
$('#main-nav li ul').each(function(){
$me = $(this);
//Count the number of li elements in this UL
var liCount = $me.find('li').size(),
//Multiply the liCount by the height + the margin on each li
ulHeight = liCount * 28;
//Store height in the data-height attribute in the UL
$me.attr("data-height", ulHeight);
});
然后,您可以使用单击功能来设置和删除高度css()
$('#main-nav li a.main-link').click(function(){
//Collapse all submenus back to 0
$('#main-nav li ul').removeAttr('style');
$(this).parent().addClass('current');
//Set height on current submenu to it's height
var $currentUl = $('li.current ul'),
currentUlHeight = $currentUl.attr('data-height');
})
CSS:
#main-nav li ul {
height: 0;
position: relative;
overflow: hidden;
opacity: 0;
filter: alpha(opacity=0);
-ms-filter: "alpha(opacity=0)";
-khtml-opacity: 0;
-moz-opacity: 0;
-webkit-transition: all .6s ease-in-out;
-moz-transition: all .6s ease-in-out;
-o-transition: all .6s ease-in-out;
-ms-transition: all .6s ease-in-out;
transition: all .6s ease-in-out;
}
#main-nav li.current ul {
opacity: 1.0;
filter: alpha(opacity=100);
-ms-filter: "alpha(opacity=100)";
-khtml-opacity: 1.0;
-moz-opacity: 1.0;
}
.ie #main-nav li.current ul { height: auto !important }
#main-nav li { height: 25px; display: block; margin-bottom: 3px }
在过渡中使用,而不是 .并设置一个比你的盒子更大的值。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>
评论
将高度设置为自动并转换最大高度。
在 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%; /**/
}
我的解决方法是转换到确切的内容高度以获得漂亮流畅的动画,然后使用回调设置为,以便内容可以自由调整大小。max-height
transitionEnd
max-height
9999px
var content = $('#content');
content.inner = $('#content .inner'); // inner div needed to get size of content when closed
// css transition callback
content.on('transitionEnd webkitTransitionEnd transitionend oTransitionEnd msTransitionEnd', function(e){
if(content.hasClass('open')){
content.css('max-height', 9999); // try setting this to 'none'... I dare you!
}
});
$('#toggle').on('click', function(e){
content.toggleClass('open closed');
content.contentHeight = content.outerHeight();
if(content.hasClass('closed')){
// disable transitions & set max-height to content height
content.removeClass('transitions').css('max-height', content.contentHeight);
setTimeout(function(){
// enable & start transition
content.addClass('transitions').css({
'max-height': 0,
'opacity': 0
});
}, 10); // 10ms timeout is the secret ingredient for disabling/enabling transitions
// chrome only needs 1ms but FF needs ~10ms or it chokes on the first animation for some reason
}else if(content.hasClass('open')){
content.contentHeight += content.inner.outerHeight(); // if closed, add inner height to content height
content.css({
'max-height': content.contentHeight,
'opacity': 1
});
}
});
.transitions {
transition: all 0.5s ease-in-out;
-webkit-transition: all 0.5s ease-in-out;
-moz-transition: all 0.5s ease-in-out;
}
body {
font-family:Arial;
line-height: 3ex;
}
code {
display: inline-block;
background: #fafafa;
padding: 0 1ex;
}
#toggle {
display:block;
padding:10px;
margin:10px auto;
text-align:center;
width:30ex;
}
#content {
overflow:hidden;
margin:10px;
border:1px solid #666;
background:#efefef;
opacity:1;
}
#content .inner {
padding:10px;
overflow:auto;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<div id="content" class="open">
<div class="inner">
<h3>Smooth CSS Transitions Between <code>height: 0</code> and <code>height: auto</code></h3>
<p>A clever workaround is to use <code>max-height</code> instead of <code>height</code>, and set it to something bigger than your content. Problem is the browser uses this value to calculate transition duration. So if you set it to <code>max-height: 1000px</code> but the content is only 100px high, the animation will be 10x too fast.</p>
<p>Another option is to measure the content height with JS and transition to that fixed value, but then you have to keep track of the content and manually resize it if it changes.</p>
<p>This solution is a hybrid of the two - transition to the measured content height, then set it to <code>max-height: 9999px</code> after the transition for fluid content sizing.</p>
</div>
</div>
<br />
<button id="toggle">Challenge Accepted!</button>
评论
scrollHeight
我没有详细阅读所有内容,但我最近遇到了这个问题,我做了以下操作:
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 到 % 或 auto 的转换不起作用。您需要使用相同的度量单位。
评论
应改用 scaleY。
ul {
background-color: #eee;
transform: scaleY(0);
transform-origin: top;
transition: transform 0.26s ease;
}
p:hover ~ ul {
transform: scaleY(1);
}
<p>Hover This</p>
<ul>
<li>Coffee</li>
<li>Tea</li>
<li>Milk</li>
</ul>
我在 jsfiddle 上制作了上述代码的供应商前缀版本,并将您的 jsfiddle 更改为使用 scaleY 而不是 height。
编辑有些人不喜欢转换内容的方式。如果这是一个问题,那么我建议改用。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>
评论
clip
max-height
扩展 @jake 的答案,过渡将一直到最大高度值,从而产生极快的动画 - 如果您同时为 :hover 和 off 设置过渡,则可以进一步控制疯狂的速度。
所以 li:hover 是鼠标进入状态时,然后非悬停属性上的过渡将是鼠标离开。
希望这会有所帮助。
例如:
.sidemenu li ul {
max-height: 0px;
-webkit-transition: all .3s ease;
-moz-transition: all .3s ease;
-o-transition: all .3s ease;
-ms-transition: all .3s ease;
transition: all .3s ease;
}
.sidemenu li:hover ul {
max-height: 500px;
-webkit-transition: all 1s ease;
-moz-transition: all 1s ease;
-o-transition: all 1s ease;
-ms-transition: all 1s ease;
transition: all 1s ease;
}
/* Adjust speeds to the possible height of the list */
这是一把小提琴:http://jsfiddle.net/BukwJ/
Jake 对 max-height 进行动画处理的答案很棒,但我发现设置较大的 max-height 导致的延迟很烦人。
可以将可折叠的内容移动到内部 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 进行下拉!我一直在使用 transform:translateY(y) 将列表转换为视图......
您可以在测试
中看到更多 http://jsfiddle.net/BVEpc/4/
我把 div 放在每个 li 后面,因为我的下拉列表来自上面,为了正确地向他们展示这是需要的,我的 div 代码是:
#menu div {
transition: 0.5s 1s;
z-index:-1;
-webkit-transform:translateY(-100%);
-webkit-transform-origin: top;
}
悬停是:
#menu > li:hover div {
transition: 0.5s;
-webkit-transform:translateY(0);
}
而且因为 ul 高度设置为它可以覆盖您的身体内容的内容,这就是我为 ul 这样做的原因:
#menu ul {
transition: 0s 1.5s;
visibility:hidden;
overflow:hidden;
}
并悬停:
#menu > li:hover ul {
transition:none;
visibility:visible;
}
过渡后的第二次是延迟,在我的下拉列表被动画关闭后,它将被隐藏......
希望以后有人能从中受益。
编辑:我简直不敢相信ppl实际上使用了这个原型!此下拉菜单仅适用于一个子菜单,仅此而已!
我更新了一个更好的菜单,它可以有两个子菜单,用于 ltr 和 rtl 方向,支持 IE 8。
LTR
小提琴 RTL
小提琴 希望将来有人发现这有用。
要从任何起始高度(包括 0)过渡到自动(全尺寸且灵活),而无需基于每个节点的硬设置代码或任何用户代码进行初始化:https://github.com/csuwildcat/transition-auto。
你想要什么:http://codepen.io/csuwldcat/pen/kwsdF
将以下 JS 文件放入页面中,然后添加/删除单个布尔属性----从要扩展和收缩的节点中。reveal=""
以用户身份执行,一旦包含示例代码下方的代码块:
/*** Nothing out of the ordinary in your styles ***/
<style>
div {
height: 0;
overflow: hidden;
transition: height 1s;
}
</style>
/*** Just add and remove one attribute and transition to/from auto! ***/
<div>
I have tons of content and I am 0px in height you can't see me...
</div>
<div reveal>
I have tons of content and I am 0px in height you can't see me...
but now that you added the 'reveal' attribute,
I magically transitioned to full height!...
</div>
将此 JS 文件拖放到您的页面中:
/*** 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);
评论
我能够做到这一点。我有一个 & 一个 div。子 div 与父 div 的宽度/高度完美契合。然后,我对属性进行动画处理以将其值向下推。它的动画非常流畅,没有像这里的任何其他解决方案那样的故障或缺点。.child
.parent
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 */
}
您可以指定 on 、 in 、 或保留为 。然后,当 div 向下滑动时,此 div 会遮盖掉它。height
.parent
px
%
auto
.child
评论
我最近一直在过渡元素而不是包装。max-height
li
ul
理由是,与 large 相比,small 的延迟远不那么明显(如果有的话),而且我还可以通过使用 或 来设置相对于 的值,而不是一些任意的巨数。max-heights
max-heights
max-height
font-size
li
ems
rems
如果我的字体大小是 ,我会将我的字体设置为类似(以容纳换行文本)。您可以在此处查看示例:1rem
max-height
3rem
http://codepen.io/mindfullsilence/pen/DtzjE
更新:自从我写了这个答案以来,CSS已经发展到包含更优雅的技术。我相信今天最好的方法是使用 css grid 和 grid-template-row,如以下答案所述: https://stackoverflow.com/a/69802208/3594432
评论
Jake 的 max-height 解决方案效果很好,如果硬编码 提供的最大高度值不比实际高度大多少 (因为否则会出现不希望的延迟和计时问题)。 另一方面,如果硬编码值意外是 不大于实际高度,元素不会完全打开。
以下仅 CSS 解决方案还需要硬编码大小 应该大于大多数出现的实际大小。然而,这 如果实际大小在某些情况下大于 硬编码大小。在这种情况下,过渡可能会跳跃一点, 但它永远不会留下部分可见的元素。 因此,此解决方案也可用于未知内容,例如从 一个数据库,你只知道内容通常不大 比 x 像素,但也有例外。
这个想法是使用负值表示 margin-bottom(或 margin-top 表示 略有不同 动画),并将 content 元素放入 带有 overflow:hidden 的中间元素。内容的负边距 元素因此降低了中间元素的高度。
以下代码使用从 -150px 到 只要内容元素不是,这本身就可以正常工作。 高于 150px。此外,它还使用 max-height 的过渡 中间元素从 0px 到 100%。这最终隐藏了中间元素 如果 content 元素高于 150px。 对于 max-height,过渡仅用于延迟其应用 关闭时减少一秒钟,而不是为了平滑的视觉效果( 因此它可以从 0px 运行到 100%)。
CSS:
.content {
transition: margin-bottom 1s ease-in;
margin-bottom: -150px;
}
.outer:hover .middle .content {
transition: margin-bottom 1s ease-out;
margin-bottom: 0px
}
.middle {
overflow: hidden;
transition: max-height .1s ease 1s;
max-height: 0px
}
.outer:hover .middle {
transition: max-height .1s ease 0s;
max-height: 100%
}
HTML格式:
<div class="outer">
<div class="middle">
<div class="content">
Sample Text
<br> Sample Text
<br> Sample Text
<div style="height:150px">Sample Test of height 150px</div>
Sample Text
</div>
</div>
Hover Here
</div>
边距底部的值应为负数,并且接近 可能为内容元素的实际高度。如果它(是绝对的 value) 较大,存在与 最大高度解决方案,但是,只要 硬编码大小并不比实际大小大多少。如果绝对值 margin-bottom 的值小于实际高度 tansition 跳了一下。无论如何,在过渡之后, content 元素要么完全显示,要么完全删除。
有关更多详细信息,请参阅我的博客文章 http://www.taccgl.org/blog/css_transition_display.html#combined_height
公认的答案适用于大多数情况,但当高度差异很大时,它就不能很好地工作了——动画速度不取决于内容的实际高度,而且它可能看起来断断续续。div
您仍然可以使用 CSS 执行实际的动画,但您需要使用 JavaScript 来计算项目的高度,而不是尝试使用 .不需要jQuery,但如果你想要兼容性,你可能需要对此进行一些修改(适用于最新版本的Chrome :))。auto
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>
评论
scrollHeight
element.style.height = element.scrollHeight + 'px'
针对每种状态使用不同的转换缓和延迟。max-height
HTML格式:
<a href="#" id="trigger">Hover</a>
<ul id="toggled">
<li>One</li>
<li>Two</li>
<li>Three</li>
<ul>
CSS:
#toggled{
max-height: 0px;
transition: max-height .8s cubic-bezier(0, 1, 0, 1) -.1s;
}
#trigger:hover + #toggled{
max-height: 9999px;
transition-timing-function: cubic-bezier(0.5, 0, 1, 0);
transition-delay: 0s;
}
请参阅示例:http://jsfiddle.net/0hnjehjc/1/
评论
999999px
每当 DOM 发生更改时,为跟踪元素设置属性。 名为 mutant-transition 的插件 您可以使用 CSS 进行过渡,而不是使用 hacks。您不必编写任何 JavaScript。只需包含 JavaScript 库并指定要在 HTML 中监视的属性即可。您不必使用固定高度的 CSS。使用 设置要在相关元素上跟踪的内容。style=""
data-mutant-attributes="X"
<div data-mutant-attributes="height">
This is an example with mutant-transition
</div>
这使用 MutationObserver 来跟踪 DOM 中的更改。您无需设置任何内容或使用 JavaScript 手动制作动画。系统会自动跟踪更改。但是,由于它使用 MutationObserver,因此这只会在 IE11+ 中转换。< IE11 将看到快照更改(无转换)。
小提琴
评论
我一直使用的解决方案是先淡出,然后缩小 和 值。它看起来与擦除不同,但它可以在没有 static 或 .font-size
padding
margin
height
max-height
工作示例:
/* final display */
#menu #list {
margin: .5em 1em;
padding: 1em;
}
/* hide */
#menu:not(:hover) #list {
font-size: 0;
margin: 0;
opacity: 0;
padding: 0;
/* fade out, then shrink */
transition: opacity .25s,
font-size .5s .25s,
margin .5s .25s,
padding .5s .25s;
}
/* reveal */
#menu:hover #list {
/* unshrink, then fade in */
transition: font-size .25s,
margin .25s,
padding .25s,
opacity .5s .25s;
}
<div id="menu">
<b>hover me</b>
<ul id="list">
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
</ul>
</div>
<p>Another paragraph...</p>
评论
*
em
不,使用定位,处理元素,并且是纯CSS:max-height
relative
li
除了Firefox之外,我没有在任何东西上进行测试,尽管从CSS来看,它应该适用于所有浏览器。
小提琴:http://jsfiddle.net/n5XfG/2596/
CSS的
.wrap { overflow:hidden; }
.inner {
margin-top:-100%;
-webkit-transition:margin-top 500ms;
transition:margin-top 500ms;
}
.inner.open { margin-top:0px; }
[HTML全文]
<div class="wrap">
<div class="inner">Some Cool Content</div>
</div>
评论
没有硬编码值。
没有 JavaScript。
没有近似值。
诀窍是使用隐藏和重复来让浏览器理解 100% 的含义。div
只要您能够复制要制作动画的元素的 DOM,此方法就适用。
.outer {
border: dashed red 1px;
position: relative;
}
.dummy {
visibility: hidden;
}
.real {
position: absolute;
background: yellow;
height: 0;
transition: height 0.5s;
overflow: hidden;
}
.outer:hover>.real {
height: 100%;
}
Hover over the box below:
<div class="outer">
<!-- The actual element that you'd like to animate -->
<div class="real">
unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable
content unpredictable content unpredictable content unpredictable content
</div>
<!-- An exact copy of the element you'd like to animate. -->
<div class="dummy" aria-hidden="true">
unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable
content unpredictable content unpredictable content unpredictable content
</div>
</div>
评论
这并不完全是问题的“解决方案”,而更像是一种解决方法。它只能像用文本编写的那样工作,但我敢肯定,可以根据需要更改为使用其他元素。
.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 并以足够快的速度转换它而不是 height、max-height 或 scaleY() 等,以使高度转换为您想要的内容。目前无法将 CSS 的实际高度转换为 auto,但转换其中的内容是,因此字体大小转换。
- 注意 - codepen 中有 javascript,但它的唯一目的是在单击手风琴时添加/删除 css 类。这可以通过隐藏的单选按钮来完成,但我并没有关注这一点,只是高度转换。
评论
当我发布这篇文章时,已经有 30 多个答案,但我觉得我的答案比杰克已经接受的答案有所改进。
我对简单地使用 和 CSS3 过渡引起的问题不满意,因为正如许多评论者所指出的那样,您必须将值设置得非常接近实际高度,否则会出现延迟。有关该问题的示例,请参阅此 JSFiddle。max-height
max-height
为了解决这个问题(虽然仍然不使用 JavaScript),我添加了另一个转换 CSS 值的 HTML 元素。transform: translateY
这意味着两者都是 和 使用:允许元素将元素向下推到它下面,同时提供我们想要的“即时”效果。问题仍然存在,但其影响已减轻。
这意味着您可以为您的价值设置更大的高度,并减少对它的担忧。max-height
translateY
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 中指定 300 毫秒的过渡持续时间,则过渡需要 300 毫秒。
- 它正在转换实际高度(与 不同),因此如果可折叠元素之后有内容,它会做正确的事情。
transform: scaleY(0)
- 虽然(像在其他解决方案中一样)有一些神奇的数字(比如“选择一个比你的盒子更高的长度”),但如果你的假设最终是错误的,这并不是致命的。在这种情况下,过渡可能看起来并不惊人,但在过渡之前和之后,这不是问题:在展开 () 状态下,整个内容始终具有正确的高度(例如,如果您选择的高度太低)。在折叠状态下,高度应为零。
height: auto
max-height
演示
这是一个演示,其中包含三个可折叠元素,它们都具有不同的高度,它们都使用相同的 CSS。在单击“运行代码段”后,您可能希望单击“整页”。请注意,JavaScript 只切换 CSS 类,不涉及度量。(你可以通过使用复选框或 )。另请注意,CSS 中负责过渡的部分非常短,HTML 只需要一个额外的包装元素。collapsed
:target
$(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>
它是如何工作的?
事实上,要实现这一点,需要进行两次过渡。其中一个将 0px(处于展开状态)转换为折叠状态(类似于此答案)。这里的 2000 是第一个幻数,它基于您的盒子不会高于此值的假设(2000 像素似乎是一个合理的选择)。margin-bottom
-2000px
单独使用转换本身有两个问题:margin-bottom
- 如果你真的有一个大于 2000 像素的盒子,那么 a 不会隐藏所有东西——即使在折叠的情况下也会有可见的东西。这是一个小修复,我们稍后会做。
margin-bottom: -2000px
- 如果实际的盒子是 1000 像素高,而你的过渡是 300 毫秒,那么可见的过渡在大约 150 毫秒后就已经结束了(或者,在相反的方向上,晚了 150 毫秒开始)。
解决第二个问题是第二个转换的用武之地,这个转换在概念上是针对包装器的最小高度(“概念上”,因为我们实际上并没有为此使用该属性;稍后会详细介绍)。min-height
下面是一个动画,展示了如何将底部边距过渡与最小高度过渡(两者的持续时间相等)结合起来,为我们提供从全高到零高度的组合过渡,具有相同的持续时间。
左侧条形图显示负下边距如何将底部向上推,从而降低可见高度。中间条形图显示了最小高度如何确保在折叠情况下,过渡不会提前结束,而在展开的情况下,过渡不会延迟开始。右侧条形图显示了两者的组合如何使框在正确的时间内从全高过渡到零高。
在我的演示中,我确定了 50px 作为最小高度上限值。这是第二个神奇的数字,它应该低于盒子的高度。50px 似乎也很合理;您似乎不太可能经常想要使一个甚至不到 50 像素高的元素可折叠。
正如您在动画中看到的,生成的过渡是连续的,但它是不可微分的——在最小高度等于由下边距调整的全高的那一刻,速度会突然发生变化。这在动画中非常明显,因为它对两个过渡都使用线性定时函数,并且整个过渡非常慢。在实际情况下(我在顶部的演示),转换只需要 300ms,并且底部边距过渡不是线性的。我为这两种转换尝试了许多不同的定时函数,我最终得到的那些感觉它们最适合最广泛的情况。
还有两个问题需要解决:
- 从上面看,高度超过 2000 像素的框在折叠状态下没有完全隐藏,
- 反之亦然的问题,在非隐藏的情况下,即使过渡没有运行,高度小于 50 像素的框也太高,因为最小高度使它们保持在 50 像素。
我们通过在折叠的情况下给容器元素 a 进行过渡来解决第一个问题。这意味着它不是真正的过渡,而是延迟应用的;它仅在过渡结束后适用。为了使它正常工作,我们还需要为相反的、非折叠的状态选择一个数字。但与 2000px 的情况不同,在 2000px 的情况下,选择过大的数字会影响过渡的质量,在这种情况下,这真的无关紧要。因此,我们可以选择一个如此之高的数字,以至于我们知道任何高度都不会接近这个高度。我选了一百万像素。如果您觉得可能需要支持高度超过一百万像素的内容,那么 1) 对不起,2) 只需添加几个零。max-height: 0
0s 0.3s
max-height
max-height
第二个问题是我们实际上没有使用最小高度过渡的原因。取而代之的是,容器中有一个伪元素,其 a 从 50px 转换为零。这与 : 它不会让容器收缩到伪元素当前具有的任何高度以下。但是因为我们使用的是 ,而不是 ,我们现在可以使用(再次延迟应用)在过渡结束后将伪元素的实际高度设置为零,确保至少在过渡之外,即使是小元素也具有正确的高度。因为比 更强,如果我们使用容器而不是伪元素的,这将不起作用。就像上一段一样,这也需要一个值来表示转换的另一端。但在这种情况下,我们可以只选择 50px。min-height
::after
height
min-height
height
min-height
max-height
min-height
max-height
min-height
height
max-height
max-height
在 Chrome(Win、Mac、Android、iOS)、Firefox(Win、Mac、Android)、Edge、IE11(除了我的演示中没有费心调试的 flexbox 布局问题)和 Safari(Mac、iOS)中进行了测试。说到 flexbox,应该可以在不使用任何 flexbox 的情况下完成这项工作;事实上,我认为你可以让几乎所有东西在 IE7 中工作——除了你不会有 CSS 过渡的事实,这使得它成为一个相当毫无意义的练习。
评论
Flexbox 解决方案
优点:
- 简单
- 没有JS
- 平稳过渡
缺点:
- 元件需要放在固定高度的柔性容器中
它的工作方式是始终具有 flex-basis:在包含内容的元素上自动,并转换 flex-grow 和 flex-shrink。
编辑:受Xbox One界面启发的改进JS Fiddle。
* {
margin: 0;
padding: 0;
box-sizing: border-box;
transition: 0.25s;
font-family: monospace;
}
body {
margin: 10px 0 0 10px;
}
.box {
width: 150px;
height: 150px;
margin: 0 2px 10px 0;
background: #2d333b;
border: solid 10px #20262e;
overflow: hidden;
display: inline-flex;
flex-direction: column;
}
.space {
flex-basis: 100%;
flex-grow: 1;
flex-shrink: 0;
}
p {
flex-basis: auto;
flex-grow: 0;
flex-shrink: 1;
background: #20262e;
padding: 10px;
width: 100%;
text-align: left;
color: white;
}
.box:hover .space {
flex-grow: 0;
flex-shrink: 1;
}
.box:hover p {
flex-grow: 1;
flex-shrink: 0;
}
<div class="box">
<div class="space"></div>
<p>
Super Metroid Prime Fusion
</p>
</div>
<div class="box">
<div class="space"></div>
<p>
Resident Evil 2 Remake
</p>
</div>
<div class="box">
<div class="space"></div>
<p>
Yolo The Game
</p>
</div>
<div class="box">
<div class="space"></div>
<p>
Final Fantasy 7 Remake + All Additional DLC + Golden Tophat
</p>
</div>
<div class="box">
<div class="space"></div>
<p>
DerpVille
</p>
</div>
评论
我知道这个问题要求没有 JavaScript 的解决方案。但对于那些感兴趣的人,这是我只使用一点 JS 的解决方案。
好的,所以默认情况下高度会改变的元素的 CSS 设置为 和 当打开时。它也有.但当然,问题在于它不会过渡到或从height: 0;
height: auto;
transition: height .25s ease-out;
height: auto;
所以我所做的是在打开或关闭时将高度设置为元素的属性。这种新的内联样式将具有更高的特异性,并覆盖 和 和 转换运行。scrollHeight
height: auto;
height: 0;
打开时,我添加了一个事件侦听器,该侦听器将仅运行一次,然后删除内联样式,将其设置回该样式,这将允许元素在必要时调整大小,就像这个带有子菜单的更复杂的示例一样 https://codepen.io/ninjabonsai/pen/GzYyVetransitionend
height: auto;
关闭时,我在下一个事件循环周期后立即使用 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>
此解决方案使用以下几种技术:
padding-bottom:100%
'hack',其中百分比是根据元素的当前宽度定义的。有关此技术的更多信息。- float shrink-wrapping,(需要额外的 div 来应用浮动清除技巧)
- 非语义地使用 https://caniuse.com/#feat=css-writing-mode 和一些转换来撤消它(这允许在垂直上下文中使用上面的填充技巧)
不过,结果是我们只使用 CSS 就可以获得高性能的过渡,并且使用单个过渡函数来顺利实现过渡;圣杯!
当然,也有缺点!我无法弄清楚如何控制内容被截断的宽度();由于填充底部的黑客,宽度和高度密切相关。不过可能有办法,所以会回来的。overflow:hidden
https://jsfiddle.net/EoghanM/n1rp3zb4/28/
body {
padding: 1em;
}
.trigger {
font-weight: bold;
}
/* .expander is there for float clearing purposes only */
.expander::after {
content: '';
display: table;
clear: both;
}
.outer {
float: left; /* purpose: shrink to fit content */
border: 1px solid green;
overflow: hidden;
}
.inner {
transition: padding-bottom 0.3s ease-in-out; /* or whatever crazy transition function you can come up with! */
padding-bottom: 0%; /* percentage padding is defined in terms of width. The width at this level is equal to the height of the content */
height: 0;
/* unfortunately, change of writing mode has other bad effects like orientation of cursor */
writing-mode: vertical-rl;
cursor: default; /* don't want the vertical-text (sideways I-beam) */
transform: rotate(-90deg) translateX(-100%); /* undo writing mode */
transform-origin: 0 0;
margin: 0; /* left/right margins here will add to height */
}
.inner > div { white-space: nowrap; }
.expander:hover .inner, /* to keep open when expanded */
.trigger:hover+.expander .inner {
padding-bottom: 100%;
}
<div class="trigger">HoverMe</div>
<div class="expander">
<div class="outer">
<div class="inner">
<div>First Item</div>
<div>Content</div>
<div>Content</div>
<div>Content</div>
<div>Long Content can't be wider than outer height unfortunately</div>
<div>Last Item</div>
</div>
</div>
</div>
<div>
after content</div>
</div>
评论
您可以通过使用 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>
您可以添加 0 高度。ul: margin 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-height: 2000px,但如果需要,您可以将该数字推到更高的值。 我在展开时对最大高度和折叠时的边距进行动画处理。
js 部分只需点击一下,可以替换为 :hover 或 checkbox 用于纯 css 解决方案。
到目前为止,我只能看到 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>
带有 、 和 的仅 CSS 替代解决方案:line-height
padding
opacity
margin
body {
background-color: linen;
}
main {
background-color: white;
}
[id^="toggle_"] ~ .content {
line-height: 0;
opacity: 0;
padding: 0 .5rem;
transition: .2s ease-out;
}
[id^="toggle_"] ~ .content > p {
margin: 0;
transition: .2s ease-out;
}
[id^="toggle_"]:checked ~ .content {
opacity: 1;
padding: .5rem;
line-height: 1.5;
}
[id^="toggle_"]:checked ~ .content p {
margin-bottom: .75rem;
}
[id^="toggle_"] + label {
display: flex;
justify-content: space-between;
padding: 0.5em 1em;
background: lightsteelblue;
border-bottom: 1px solid gray;
cursor: pointer;
}
[id^="toggle_"] + label:before {
content: "Show";
}
[id^="toggle_"]:checked + label:before {
content: "Hide";
}
[id^="toggle_"] + label:after {
content: "\25BC";
}
[id^="toggle_"]:checked + label:after {
content: "\25B2";
}
<main>
<div>
<input type="checkbox" id="toggle_1" hidden>
<label for="toggle_1" hidden></label>
<div class="content">
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis dolor neque, commodo quis leo ut, auctor tincidunt mauris. Nunc fringilla tincidunt metus, non gravida lorem condimentum non. Duis ornare purus nisl, at porta arcu eleifend eget. Integer lorem ante, porta vulputate dui ut, blandit tempor tellus. Proin facilisis bibendum diam, sit amet rutrum est feugiat ut. Mauris rhoncus convallis arcu in condimentum. Donec volutpat dui eu mollis vulputate. Nunc commodo lobortis nunc at ultrices. Suspendisse in lobortis diam. Suspendisse eget vestibulum ex.
</p>
</div>
</div>
<div>
<input type="checkbox" id="toggle_2" hidden>
<label for="toggle_2" hidden></label>
<div class="content">
<p>
Maecenas laoreet nunc sit amet nulla ultrices auctor. Vivamus sed nisi vitae nibh condimentum pulvinar eu vel lorem. Sed pretium viverra eros ut facilisis. In ut fringilla magna. Sed a tempor libero. Donec sapien libero, lacinia sed aliquet ut, imperdiet finibus tellus. Nunc tellus lectus, rhoncus in posuere quis, tempus sit amet enim. Morbi et erat ac velit fringilla dignissim. Donec commodo, est id accumsan cursus, diam dui hendrerit nisi, vel hendrerit purus dolor ut risus. Phasellus mattis egestas ipsum sed ullamcorper. In diam ligula, rhoncus vel enim et, imperdiet porta justo. Curabitur vulputate hendrerit nisl, et ultricies diam. Maecenas ac leo a diam cursus ornare nec eu quam.
</p>
<p>Sed non vulputate purus, sed consectetur odio. Sed non nibh fringilla, imperdiet odio nec, efficitur ex. Suspendisse ut dignissim enim. Maecenas felis augue, tempor sit amet sem fringilla, accumsan fringilla nibh. Quisque posuere lacus tortor, quis malesuada magna elementum a. Nullam id purus in ante molestie tincidunt. Morbi luctus orci eu egestas dignissim. Sed tincidunt, libero quis scelerisque bibendum, ligula nisi gravida libero, id lacinia nulla leo in elit.
</p>
<p>Aenean aliquam risus id consectetur sagittis. Aliquam aliquam nisl eu augue accumsan, vel maximus lorem viverra. Aliquam ipsum dolor, tempor et justo ac, fermentum mattis dui. Etiam at posuere ligula. Vestibulum tortor metus, viverra vitae mi non, laoreet iaculis purus. Praesent vel semper nibh. Curabitur a congue lacus. In et pellentesque lorem. Morbi posuere felis non diam vulputate, non vulputate ex vehicula. Vivamus ultricies, massa id sagittis consequat, sem mauris tincidunt nunc, eu vehicula augue quam ut mauris.
</p>
</div>
</div>
<div>
<input type="checkbox" id="toggle_3" hidden>
<label for="toggle_3" hidden></label>
<div class="content">
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis dolor neque, commodo quis leo ut, auctor tincidunt mauris. Nunc fringilla tincidunt metus, non gravida lorem condimentum non. Duis ornare purus nisl, at porta arcu eleifend eget. Integer lorem ante, porta vulputate dui ut, blandit tempor tellus. Proin facilisis bibendum diam, sit amet rutrum est feugiat ut. Mauris rhoncus convallis arcu in condimentum. Donec volutpat dui eu mollis vulputate. Nunc commodo lobortis nunc at ultrices. Suspendisse in lobortis diam. Suspendisse eget vestibulum ex.
</p>
<p>Sed non vulputate purus, sed consectetur odio. Sed non nibh fringilla, imperdiet odio nec, efficitur ex. Suspendisse ut dignissim enim. Maecenas felis augue, tempor sit amet sem fringilla, accumsan fringilla nibh. Quisque posuere lacus tortor, quis malesuada magna elementum a. Nullam id purus in ante molestie tincidunt. Morbi luctus orci eu egestas dignissim. Sed tincidunt, libero quis scelerisque bibendum, ligula nisi gravida libero, id lacinia nulla leo in elit.
</p>
</div>
</div>
</main>
评论
pointer-events: none
auto
我使用的方法是让所有内容都基于字体大小(使用 em 作为单位),或者至少是影响我们想要打开动画的框的垂直大小的所有内容。然后,如果任何内容不在 em 单位中(例如 1px 边框),我会在关闭框时将其设置为 0,方法是用 * 定位所有子项。
动画动画是 0 到 100 之间的字体大小(以 % 为单位)。什么?父级的已知字体大小。
它的工作规则是动画框中的所有内容都必须:
- 将 % 用于所有字体大小和行高
- 将 EM 用于垂直的所有内容(如高度、边距和填充顶部/底部等)
- 如果将 px 用于垂直的东西(例如边框顶部/底部),则应在框关闭时覆盖它(例如
border-top:0;border-bottom:0;
)
在某种程度上,您仍然可以使用像素作为参考单位,只需将包装器字体大小设置为 100px,例如 因此,如果您想在内部任何地方使用 10px,则可以使用 .#page{font-size:100px;}
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。我相信我在两个易于理解的用例中发现了这一点。
切换叠加层
.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>
在文档流中切换
.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>
我写了一篇关于这些技术的博客文章。
评论
根据 MDN Web Docs 的说法,值已被有意从 CSS 过渡规范中排除,因此在网格和 flex 布局中使用 、 或属性来代替 、 或属性。auto
height: auto
height: 100%
top
flex
展开/折叠叠加层
.grid-container {
display: grid;
position: absolute;
}
.content {
background: aqua;
height: 0;
overflow: hidden;
transition: 1s;
}
span:hover + .grid-container .content {
height: 100%;
}
<span>Hover over me!</span>
<div class="grid-container">
<div class="content">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</div>
</div>
<p>Rest of the page content...</p>
展开/折叠滑动叠加层
.grid-container {
display: grid;
position: absolute;
overflow: hidden;
pointer-events: none; /* to enable interaction with elements below the container */
}
.content {
background: aqua;
pointer-events: auto;
position: relative;
top: -100%;
transition: 1s;
}
span:hover + .grid-container .content {
top: 0;
}
<span>Hover over me!</span>
<div class="grid-container">
<div class="content">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</div>
</div>
<p>Rest of the page content...</p>
评论
Expanding/collapsing in the document flow
div
html
body
我想添加一个关于如何扩展/折叠保持文档流的示例,此示例适用于使用 tailwindcss 的 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% 之间起作用,两个 数值。由于“auto”不是数值,因此小数 0 和“auto”之间不存在增量。100%是灵活的 值,因此不需要特定的高度。
其次,隐藏内容的外部容器和内部容器都必须设置为 display: flex with flex-direction: column。
第三,外容器必须具有 height 属性。仅当所有内容都包含在外部容器中时,将其设置为 0 才能保持平滑过渡,因为弯曲行为优先于高度。编辑:Json 建议使用 height: fit-content,这样容器下方的任何内容也会被下推。
.outer-container { height: 0; display: flex; flex-direction: column; }
.inner-container { display: flex; flex-direction: column; }
.hidden-content {
height: 0;
opacity: 0;
transition: height 1s 0.5s ease-in-out, opacity 0.5s ease-in-out;
/* transition out: first fade out opacity, then shrink height after a delay equal to the opacity duration */
}
.trigger:hover + .inner-container > .hidden-content {
height: 100%;
opacity: 1;
transition: height 1s ease-in-out, opacity 0.5s 1s ease-in-out;
/* transition in: first expand height, then fade in opacity after a delay equal to the height duration */
}
<div class="outer-container">
<a href="#" class="trigger">Hover to Reveal Inner Container's Hidden Content</a>
<div class="inner-container">
<div class="hidden-content">This is hidden content. When triggered by hover, its height transitions from 0 to 100%, which pushes other content in the same container down gradually.</div>
<div>Within the same container, this other content is pushed down gradually as the hidden content's height transitions from 0 to 100%.</div>
</div>
</div>
按“运行代码片段”按钮,查看转换的运行情况。 它只是 CSS,没有特定的高度。
评论
overflow: hidden
我想分享我最终使用的方法(但还没有看到)
首先,我要说的是,没有javascript就不可能翻译到:auto
- https://bugs.webkit.org/show_bug.cgi?id=16020
- https://bugzilla.mozilla.org/show_bug.cgi?id=571344
- https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Transitions/Using_CSS_transitions#which_css_properties_can_be_transitioned
它可以与CSS过渡一起使用,但需要一些JavaScript来更改“scrollheight”auto
它的工作方式:
- 容器具有 .
overflow: hidden
- 将元素的高度设置为 。
scrollHeight
- 要折叠,请将高度设置为下一个动画帧中的高度
0px
toggle()
切换类只是为了跟踪状态collapsed
这样 CSS 转换将起作用,因为它是从 to 而不是 .0px
scrollHeight
auto
function collapse(el, collapse) {
el.style.height = el.scrollHeight+'px';
if(!collapse)
requestAnimationFrame(() => el.style.height = '0px');
}
function toggle(btn) {
const ul = btn.nextElementSibling,
collapsed = ul.classList.contains('collapsed');
collapse(ul, collapsed);
ul.classList.toggle('collapsed');
}
ul {
transition: height 0.16s ease-out;
overflow:hidden;
height:auto;
}
<ul>
<li>Item 1
<li>Item 2
<li>Item 3
</ul>
<button onclick="toggle(this)">Toggle</button>
<ul>
<li>Item 4
<li>Item 5
<li>Item 6
</ul>
<ul>
<li>Item 7
<li>Item 8
<li>Item 9
</ul>
评论
auto
自从这个问题首次提出以来,浏览器已经取得了相当大的进步。您现在可以转换网格轨道大小,并且得到了很好的支持,这意味着可以使用 CSS 网格布局解决此问题
在这里,我将第二个网格项从(0 个小数单位)转换为(1 个小数单位)。0fr
1fr
#parent0 {
display: grid;
grid-template-rows: min-content 0fr;
transition: grid-template-rows 500ms;
}
#child0 {
background-color: #dedede;
overflow: hidden;
}
#parent0:hover{
grid-template-rows: min-content 1fr;
}
<div id="parent0">
<h1>Hover me</h1>
<div id="child0">Some content
<br>Some content
<br>Some content
<br>Some content
<br>Some content
</div>
</div>
评论
我喜欢 jake 和 dotnetCarpenter 的解决方案。 但是 Jake 的解决方案缺乏对转换的控制,而 dotnetCarpenter 的解决方案不会删除空间。 所以我有点融合了这两种对我来说效果很好的解决方案。 这是我得到的:
---------------------- 基于CSS类的解决方案 ------------------------
HTML格式:
<div>
<div id="title">
Show items
</div>
<ul class="theul hideul">
<li>one</li>
<li>two</li>
<li>three</li>
</ul>
</div>
CSS:
ul{list-style: none;}
.hide {
max-height: 0;
transform: scale(1, 0);
transform-origin: top;
transition: transform 0.3s ease-in, max-height 0.3s ease-in;
> li {
transform: scale(1, 0);
transition: transform 0.3s ease-in;
}
}
.show {
max-height: 999px;
transform: scale(1, 1);
transform-origin: top;
transition: transform 0.3s ease-in, max-height 0.3s ease-in;
> li {
transform: scale(1, 1);
transition: transform 0.3s ease-in;
}
}
添加事件侦听器以在 和 类之间切换。#title
hide
show
---------------------- 只有CSS解决方案 ------------------------
如果你不想要任何 JavaScript,那么这只是 CSS 解决方案(相同的解决方案):
HTML格式:
<div>
<div id="title">
Show items
</div>
<ul>
<li>one</li>
<li>two</li>
<li>three</li>
</ul>
</div>
CSS:
ul {
list-style: none;
max-height: 0;
transform: scale(1, 0);
transform-origin: top;
transition: transform 0.3s ease-out, max-height 0.3s ease-in-out;
/* transition-delay: 0.1s; */
> li {
transform: scale(1, 0);
transition: transform 0.3s ease-in;
}
}
#title2:hover + ul {
max-height: 999px;
transform: scale(1, 1);
transform-origin: top;
transition: transform 0.3s ease-in, max-height 0.3s ease-in-out;
/* transition-delay: 0.1s; */
> li {
transform: scale(1, 1);
transition: transform 0.3s ease-in;
}
}
旁注:如果你不关心过渡,那么这是编写下拉式UI的最佳方式之一。
[HTML全文]details
<details>
<summary>
<p>show items</p>
</summary>
<ul>
<li>item 1</li>
<li>item 2</li>
<li>item 3</li>
</ul>
</details>
如果要添加自定义下拉图标,请先删除默认图标:
summary::marker {
content: none;
}
没有找到任何好的CSS解决方案,我用jQuery做了这个
$(document).ready(function() {
$('.nav_menu .menu-item-has-children').each(function(index) {
var smheight = Math.ceil($(this).find(".sub-menu").outerHeight());
$(this).find(".sub-menu").height(0);
$(this).mouseenter(function() {
$(this).find(".sub-menu").animate({
height: smheight + "px"
}, 250);
});
});
$('.nav_menu .menu-item-has-children').each(function(index) {
$(this).mouseleave(function() {
$(this).find(".sub-menu").animate({
height: "0"
}, 250);
});
});
});
.menu-item-has-children {
background-color: violet;
}
li ul {
overflow: hidden;
background-color: lime;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul class="nav_menu">
<li class="menu-item-has-children">
<a href="#">Main Thing</a>
<ul class="sub-menu">
<li><a href="#">Sub Thing</a></li>
<li><a href="#">Sub Thing</a></li>
<li><a href="#">Sub Thing</a></li>
</ul>
</li>
<li class="menu-item-has-children">
<a href="#">Main Thing</a>
<ul class="sub-menu">
<li><a href="#">Sub Thing</a></li>
<li><a href="#">Sub Thing</a></li>
<li><a href="#">Sub Thing</a></li>
</ul>
</li>
</ul>
解决此问题的另一种方法是使用模板。您可以用作过渡的起点grid
display: grid;
grid-template-rows: 0fr;
作为过渡的收尾,添加grid-template-rows: 1fr;
在内容的包装器中,您应该添加 .这是我自己的scss代码:overflow: hidden
.accordion-content {
opacity: 0;
transition: opacity 0.3s ease, grid-template-rows 0.3s ease;
display: grid;
grid-template-rows: 0fr;
.section-content {
overflow: hidden;
}
&.show {
opacity: 1;
grid-template-rows: 1fr;
}
}
上一个:如何禁用文本选择突出显示
评论
height:auto/max-height
height
max-height
300px
50px
max-height
50px
height
height:auto