我正在尝试使用CSS转换制作一个<ul>幻灯片。

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

如果我从高度做:40px;至高度:自动;,那么它将向上滑动到高度:0;,然后突然跳到正确的高度。

如果不使用JavaScript,我怎么能做到这一点?

#子0{高度:0;溢出:隐藏;背景色:#dedede;-moz过渡:高度1s容易;-webkit过渡:高度1s轻松;-o过渡:高度1s容易;过渡:高度1s;}#parent0:悬停#child0{高度:自动;}#孩子40{高度:40px;溢出:隐藏;背景色:#dedede;-moz过渡:高度1s容易;-webkit过渡:高度1s轻松;-o过渡:高度1s容易;过渡:高度1s;}#parent40:悬停#child40{高度:自动;}h1小时{字号:粗体;}两个CSS片段之间的唯一区别是一个高度为0,另一个高度是40。<小时><div id=“parent0”><h1>将我悬停(高度:0)</h1><div id=“child0”>一些内容<br>一些内容<br>一些内容<br>一些内容<br>一些内容<br>一些内容<br></div></div><小时><div id=“parent40”><h1>将我悬停(高度:40)</h1><div id=“child40”>一些内容<br>一些内容<br>一些内容<br>一些内容<br>一些内容<br>一些内容<br></div></div>


当前回答

要从任何起始高度(包括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);

其他回答

我意识到这条线索已经过时了,但它在某些谷歌搜索中排名靠前,所以我认为它值得更新。

还可以只获取/设置元素自身的高度:

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

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

我最近一直在转换li元素的最大高度,而不是包装ul。

原因是,与大的最大高度相比,小的最大高度的延迟要少得多(如果有的话),而且我还可以使用ems或rems来设置与li字体大小相关的最大高度值,而不是任意的大数字。

如果我的字体大小是1rem,我会将最大高度设置为3rem(以容纳换行文本)。您可以在这里看到一个示例:

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

你可以,用一点非语义的小把戏。我通常的方法是设置外部DIV的高度动画,该外部DIV有一个子级,这是一个仅用于测量内容高度的无样式DIV。

函数growDiv(){var growDiv=文档.getElementById('grow');if(growtDiv.clientHeight){growtDiv.style.height=0;}其他{var wrapper=document.querySelector('.measureingWrapper');growDiv.style.height=wrapper.clientHeight+“px”;}}#成长{-moz过渡:高度.5s;-ms过渡:高度.5s;-o过渡:高度.5s;-webkit过渡:高度.5s;过渡:高度.5s;高度:0;溢出:隐藏;轮廓:1px纯红色;}<input type=“button”onclick=“growDiv()”value=“grow”><div id='grow'><div class='measureingWrapper'><div>我部门的内容。</div><div>我部门的内容。</div><div>我部门的内容。</div><div>我部门的内容。</div><div>我部门的内容。</div><div>我部门的内容。</div></div></div>

人们希望能够省去.metringWrapper,只需将DIV的高度设置为auto并设置动画,但这似乎不起作用(设置了高度,但没有动画发生)。

函数growDiv(){var growDiv=文档.getElementById('grow');if(growtDiv.clientHeight){growtDiv.style.height=0;}其他{growDiv.style.height=“自动”;}}#成长{-moz过渡:高度.5s;-ms过渡:高度.5s;-o过渡:高度.5s;-webkit过渡:高度.5s;过渡:高度.5s;高度:0;溢出:隐藏;轮廓:1px纯红色;}<input type=“button”onclick=“growDiv()”value=“grow”><div id='grow'><div>我部门的内容。</div><div>我部门的内容。</div><div>我部门的内容。</div><div>我部门的内容。</div><div>我部门的内容。</div><div>我部门的内容。</div></div>

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

这是一个仅限CSS的解决方案,具有以下财产:

开始时没有延迟,过渡也不会提前停止。在两个方向上(展开和折叠),如果在CSS中指定300毫秒的转换持续时间,则转换需要300毫秒。它正在转换实际高度(与transform:scaleY(0)不同),因此如果可折叠元素后面有内容,它会做正确的事情。虽然(和其他解决方案一样)有神奇的数字(比如“选择一个比你的盒子永远都要高的长度”),但如果你的假设最终是错误的,这并不是致命的。在这种情况下,转换看起来可能并不令人惊讶,但在转换之前和之后,这不是问题:在展开(高度:自动)状态下,整个内容始终具有正确的高度(不同于例如,如果选择的最大高度太低)。在塌陷状态下,高度应该为零。

Demo

这里有一个演示,其中有三个可折叠的元素,它们都有不同的高度,都使用相同的CSS。您可能希望在单击“运行代码段”后单击“完整页面”。请注意,JavaScript仅切换折叠的CSS类,不涉及度量。(通过使用复选框或:target,您可以在完全没有JavaScript的情况下进行这个精确的演示)。还要注意,负责转换的CSS部分很短,HTML只需要一个额外的包装元素。

$(函数(){$(“.tggler”).click(函数(){$(this).next().tggleClass(“塌陷”);$(this).tggleClass(“已切换”);//这只会旋转展开器箭头});});.可折叠包装纸{显示:柔性;溢出:隐藏;}.可折叠包装:在之后{内容:“”;高度:50px;过渡:高度0.3s线性,最大高度0s 0.3s线性;最大高度:0px;}.可折叠{过渡:边缘底部0.3s立方bezier(0,0,0、1);边距底部:0;最大高度:1000000px;}.scollapsible-wrappercollapsed>.scollapsble{边距底部:-2000px;过渡:边缘底部0.3s立方bezier(1,0,1,1),能见度0s 0.3s,最大高度0s 0.3s;可见性:隐藏;最大高度:0;}.可折叠包装器.折叠:之后{高度:0;过渡:高度0.3s线性;最大高度:50px;}/*可折叠实现结束;下面的东西只是这个演示的样式*/#集装箱{显示:柔性;对齐项目:弹性开始;最大宽度:1000px;边距:0自动;} .菜单{边框:1px实心#ccc;方框阴影:0 1px 3px rgba(0,0,0,0.5);边距:20px;}.菜单项{显示:块;背景:线性梯度(到底部,#fff 0%,#eee 100%);边距:0;填充:1em;线高:1.3;}.可折叠.菜单项{左侧边框:2px实心#888;右边框:2px实心#888;背景:线性梯度(至底部,#eee 0%,#ddd 100%);}菜单项日志{背景:线性梯度(至底部,#aaa 0%,#888 100%);颜色:白色;光标:指针;}.menu item.tggler:之前{内容:“”;显示:块;左侧边框:8px纯白色;边框顶部:8px实心透明;边框底部:8px实心透明;宽度:0;高度:0;浮动:右侧;转变:转变0.3s缓和;}.menu item.tggler.toggled:之前{变换:旋转(90度);}正文{字体系列:无衬线;字体大小:14px;}*,*:之后{框大小调整:边框框;}<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”>涉及全息甲板的东西</div><div class=“menu item”>派遣一支客场球队</div><div class=“menu item toggler”>高级解决方案</div><div class=“可折叠包装器已折叠”><div class=“可折叠”><div class=“menu item”>单独的茶托</div><div class=“menu item”>派遣一支包括队长在内的客场球队(尽管Riker表示抗议)</div><div class=“menu item”>Ask Worf</div><div class=“menu item”>涉及19世纪卫斯理和全息甲板的东西</div><div class=“menu item”>向Q寻求帮助</div></div></div><div class=“menu item”>甜言蜜语外星人侵略者</div><div class=“menu item”>从辅助系统重新布线</div></div><div class=“menu”><div class=“menu item”>涉及全息甲板的东西</div><div class=“menu item”>派遣一支客场球队</div><div class=“menu item toggler”>高级解决方案</div><div class=“可折叠包装器已折叠”><div class=“可折叠”><div class=“menu item”>单独的茶托</div><div class=“menu item”>派出一支包括队长在内的客场球队(尽管Riker表示抗议)</div></div></div><div class=“menu item”>甜言蜜语外星人侵略者</div><div class=“menu item”>从辅助系统重新布线</div></div><div class=“menu”><div class=“menu item”>涉及全息甲板的东西</div><div class=“menu item”>派遣一支客场球队</div><div class=“menu item toggler”>高级解决方案</div><div class=“可折叠包装器已折叠”><div class=“可折叠”><div class=“menu item”>单独的茶托</div><div class=“menu item”>派出一支包括队长在内的客场球队(尽管Riker表示抗议)</div><div class=“menu item”>Ask Worf</div><div class=“menu item”>涉及19世纪卫斯理和全息甲板的东西</div><div class=“menu item”>向Q寻求帮助</div><div class=“menu item”>单独的茶托</div><div class=“menu item”>派遣一支包括队长在内的客场球队(尽管Riker表示抗议)</div><div class=“menu item”>Ask Worf</div><div class=“menu item”>涉及19世纪卫斯理和全息甲板的东西</div><div class=“menu item”>向Q寻求帮助</div></div></div><div class=“menu item”>甜言蜜语外星人侵略者</div><div class=“menu item”>从辅助系统重新布线</div></div></div>

它是如何工作的?

事实上,实现这一点涉及两个过渡。其中一个将边距底部从0px(展开状态)转换为-2000px(折叠状态)(类似于此答案)。这里的2000是第一个神奇的数字,它基于这样的假设,即你的盒子不会高于这个数字(2000像素似乎是一个合理的选择)。

单独使用保证金底部转换有两个问题:

如果你有一个高于2000像素的框,那么底部的边距:-2000px不会隐藏所有内容,即使在折叠的情况下也会有可见的内容。这是我们稍后要做的一个小修复。如果实际的框是1000像素高,并且你的过渡是300毫秒长,那么在大约150毫秒之后,可见的过渡已经结束(或者,在相反的方向上,延迟150毫秒开始)。

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

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

左栏显示负底部边距如何向上推动底部,从而降低可见高度。中间的条形图显示了最小高度如何确保在塌陷情况下过渡不会提前结束,而在扩展情况下,过渡不会延迟开始。右栏显示了两者的组合如何在正确的时间内使长方体从全高过渡到零高。

对于我的演示,我已经确定了50像素作为上限最小高度值。这是第二个神奇的数字,它应该低于盒子的高度。50px似乎也很合理;您似乎不太可能经常想要让一个元素在一开始甚至不到50像素高的情况下进行折叠。

正如您在动画中看到的,生成的过渡是连续的,但它是不可微分的——当最小高度等于由底部边距调整的全高度时,速度会突然变化。这在动画中非常明显,因为它对两个过渡都使用了线性计时函数,而且整个过渡非常缓慢。在实际情况下(我在顶部的演示),转换只需要300毫秒,而底部边缘转换不是线性的。我在这两种转换中都使用了很多不同的计时功能,而我最终使用的这些功能似乎最适合最广泛的情况。

还有两个问题需要解决:

从上面看,高度超过2000像素的盒子在折叠状态下没有完全隐藏,相反的问题是,在非隐藏的情况下,即使在转换未运行时,高度小于50像素的框也太高,因为最小高度使它们保持在50像素。

我们通过给容器元素一个最大高度来解决第一个问题:在折叠的情况下为0,0到0.3s的过渡。这意味着这不是真正的过渡,但最大高度是有延迟的;它只在过渡结束后才适用。为了正确工作,我们还需要为相反的非折叠状态选择一个数值最大高度。但与2000px的情况不同,选取过大的数字会影响转换的质量,在这种情况下,这真的无关紧要。因此,我们可以选择一个高度如此之高的数字,我们知道任何高度都不会接近这个数字。我选了一百万像素。如果你觉得你可能需要支持超过100万像素的内容,那么1)对不起,2)只需添加几个零。

第二个问题是为什么我们实际上没有使用最小高度作为最小高度过渡。相反,容器中有一个::after伪元素,其高度从50px转换为零。这与最小高度具有相同的效果:它不会让容器收缩到伪元素当前具有的任何高度以下。但是因为我们使用的是高度,而不是最小高度,所以我们现在可以使用最大高度(再次使用延迟)在过渡结束后将伪元素的实际高度设置为零,确保至少在过渡之外,即使是小元素也具有正确的高度。因为min-height比max-height强,所以如果我们使用容器的min-heith而不是伪元素的高度,这将不起作用。就像上一段中的最大高度一样,该最大高度也需要过渡另一端的值。但在这种情况下,我们可以选择50像素。

在Chrome(Win、Mac、Android、iOS)、Firefox(Win,Mac、Android)、Edge、IE11(除了我的演示中的flexbox布局问题,我没有调试)和Safari(Mac、iOS)中进行测试。说到flexbox,应该可以在不使用任何flexbox的情况下实现这一功能;事实上,我认为你可以在IE7中让几乎所有的东西都正常工作,除了你不会有CSS转换,这是一个毫无意义的练习。

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

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

parent{border top:#999 1px solid;}h1{margin:.5rem;字体大小:1.3rem}.儿童{高度:0;溢出:隐藏;背景色:#dedede;过渡:填充。2s易进易出,不透明度。2s容易进易出;填充:0.5雷姆;不透明度:0;}.childre::之前,.childres::之后{content:“”;显示:块;}.children::在{margin-top:-2rem;}之前.children::在{margin-bottom:-2rem;}之后.父级:悬停.子级{高度:自动;不透明度:1;衬垫:2.5雷姆。5雷姆;/*0.5+abs(-2),确保低于预期最小高度*/}<div class=“parent”><h1>将我悬停</h1><div class=“children”>一些内容<br>一些内容<br>一些内容<br>一些内容<br>一些内容<br>一些内容<br></div></div><div class=“parent”><h1>将我悬停(长内容)</h1><div class=“children”>一些内容<br>部分内容<br>部分内容<br>部分内容<br>部分内容<br>部分内容<br>部分内容<br>部分内容<br>部分内容<br>部分内容<br>部分内容<br></div></div><div class=“parent”><h1>悬停我(简短内容)</h1><div class=“children”>一些内容<br>一些内容<br>一些内容<br></div></div>