How can I transition height: 0; to height: auto; u

2018-12-30 23:37发布

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

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

If I do it from height: 40px; to height: auto;, then it will slide up to height: 0;, and then suddenly jump to the correct height.

How else could I do this without using JavaScript?

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

30条回答
皆成旧梦
2楼-- · 2018-12-31 00:00

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

HTML:

<a href="#" id="trigger">Hover</a>
<ul id="toggled">
    <li>One</li>
    <li>Two</li>
    <li>Three</li>
<ul>

CSS:

#toggled{
    max-height: 0px;
    transition: max-height .8s cubic-bezier(0, 1, 0, 1) -.1s;
}

#trigger:hover + #toggled{
    max-height: 9999px;
    transition-timing-function: cubic-bezier(0.5, 0, 1, 0); 
    transition-delay: 0s;
}

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

查看更多
看淡一切
3楼-- · 2018-12-31 00:01

My workaround is to transition max-height to the exact content height for a nice smooth animation, then use a transitionEnd callback to set max-height to 9999px so the content can resize freely.

var content = $('#content');
content.inner = $('#content .inner'); // inner div needed to get size of content when closed

// css transition callback
content.on('transitionEnd webkitTransitionEnd transitionend oTransitionEnd msTransitionEnd', function(e){
    if(content.hasClass('open')){
        content.css('max-height', 9999); // try setting this to 'none'... I dare you!
    }
});

$('#toggle').on('click', function(e){
    content.toggleClass('open closed');
    content.contentHeight = content.outerHeight();
    
    if(content.hasClass('closed')){
        
        // disable transitions & set max-height to content height
        content.removeClass('transitions').css('max-height', content.contentHeight);
        setTimeout(function(){
            
            // enable & start transition
            content.addClass('transitions').css({
                'max-height': 0,
                'opacity': 0
            });
            
        }, 10); // 10ms timeout is the secret ingredient for disabling/enabling transitions
        // chrome only needs 1ms but FF needs ~10ms or it chokes on the first animation for some reason
        
    }else if(content.hasClass('open')){  
        
        content.contentHeight += content.inner.outerHeight(); // if closed, add inner height to content height
        content.css({
            'max-height': content.contentHeight,
            'opacity': 1
        });
        
    }
});
.transitions {
    transition: all 0.5s ease-in-out;
    -webkit-transition: all 0.5s ease-in-out;
    -moz-transition: all 0.5s ease-in-out;
}

body {
    font-family:Arial;
    line-height: 3ex;
}
code {
    display: inline-block;
    background: #fafafa;
    padding: 0 1ex;
}
#toggle {
    display:block;
    padding:10px;
    margin:10px auto;
    text-align:center;
    width:30ex;
}
#content {
    overflow:hidden;
    margin:10px;
    border:1px solid #666;
    background:#efefef;
    opacity:1;
}
#content .inner {
    padding:10px;
    overflow:auto;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<div id="content" class="open">
    <div class="inner">
        <h3>Smooth CSS Transitions Between <code>height: 0</code> and <code>height: auto</code></h3>
        <p>A clever workaround is to use <code>max-height</code> instead of <code>height</code>, and set it to something bigger than your content. Problem is the browser uses this value to calculate transition duration. So if you set it to <code>max-height: 1000px</code> but the content is only 100px high, the animation will be 10x too fast.</p>
        <p>Another option is to measure the content height with JS and transition to that fixed value, but then you have to keep track of the content and manually resize it if it changes.</p>
        <p>This solution is a hybrid of the two - transition to the measured content height, then set it to <code>max-height: 9999px</code> after the transition for fluid content sizing.</p>
    </div>
</div>

<br />

<button id="toggle">Challenge Accepted!</button>

查看更多
其实,你不懂
4楼-- · 2018-12-31 00:01

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

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

and hover is :

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

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

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

and hover:

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

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

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

查看更多
与君花间醉酒
5楼-- · 2018-12-31 00:02

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

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

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

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

查看更多
冷夜・残月
6楼-- · 2018-12-31 00:02

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

Something like this, pseudo code

.parent{ position:relative; overflow:hidden; } 
/** shown state */
.child {
  position:absolute;top:0;:left:0;right:0;bottom:0;
  height: 100%;
  transition: transform @overlay-animation-duration ease-in-out;
  .translate(0, 0);
}

/** Animate to hidden by sliding down: */
.child.slidedown {
  .translate(0, 100%); /** Translate the element "out" the bottom of it's .scene container "mask" so its hidden */
}

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

查看更多
回忆,回不去的记忆
7楼-- · 2018-12-31 00:05

Flexbox Solution

Pros:

  • simple
  • no JS
  • smooth transition

Cons:

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

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

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

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  transition: 0.25s;
  font-family: monospace;
}

body {
  margin: 10px 0 0 10px;
}

.box {
  width: 150px;
  height: 150px;
  margin: 0 2px 10px 0;
  background: #2d333b;
  border: solid 10px #20262e;
  overflow: hidden;
  display: inline-flex;
  flex-direction: column;
}

.space {
  flex-basis: 100%;
  flex-grow: 1;
  flex-shrink: 0;    
}

p {
  flex-basis: auto;
  flex-grow: 0;
  flex-shrink: 1;
  background: #20262e;
  padding: 10px;
  width: 100%;
  text-align: left;
  color: white;
}

.box:hover .space {
  flex-grow: 0;
  flex-shrink: 1;
}
  
.box:hover p {
  flex-grow: 1;
  flex-shrink: 0;    
}
<div class="box">
  <div class="space"></div>
  <p>
    Super Metroid Prime Fusion
  </p>
</div>
<div class="box">
  <div class="space"></div>
  <p>
    Resident Evil 2 Remake
  </p>
</div>
<div class="box">
  <div class="space"></div>
  <p>
    Yolo The Game
  </p>
</div>
<div class="box">
  <div class="space"></div>
  <p>
    Final Fantasy 7 Remake + All Additional DLC + Golden Tophat
  </p>
</div>
<div class="box">
  <div class="space"></div>
  <p>
    DerpVille
  </p>
</div>

JS Fiddle

查看更多
登录 后发表回答