it-swarm.com.ru

Как я могу перейти высоту: 0; по высоте: авто; используя CSS?

Я пытаюсь сделать скольжение <ul> с помощью CSS-переходов.

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

Используйте max-height в преобразовании, а не height. И установите значение max-height в нечто большее, чем когда-либо получит ваш ящик.

Смотрите JSFiddle demo предоставленный Крисом Джорданом в другом ответ здесь.

#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>
2462
jake

Вместо этого вы должны использовать scaleY.

HTML:

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

CSS:

ul {
    background-color: #eee;
    transform: scaleY(0);    
    transform-Origin: top;
    transition: transform 0.26s ease;
}

p:hover ~ ul {
    transform: scaleY(1);
}

Я сделал версию вышеуказанного кода с префиксом поставщика в jsfiddle, http://jsfiddle.net/dotnetCarpenter/PhyQc/9/ и изменил ваш jsfiddle, чтобы использовать scaleY вместо height, http://jsfiddle.net/dotnetCarpenter/7cnfc/206/ .

269
dotnetCarpenter

В настоящее время вы не можете анимировать по высоте, если одна из высот задействована auto, вам нужно установить две явные высоты.

183
robertc

Решением, которое я всегда использовал, было сначала исчезнуть, а затем уменьшить значения 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>
104
Steven Vachon

Вы можете, с небольшим количеством несемантических 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 на auto и иметь эту анимацию, но это, похоже, не работает (высота устанавливается, но анимация не происходит).

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.

73
jhurshman

Я знаю, что это тридцатилетний ответ на этот вопрос, но я думаю, что оно того стоит. Это решение только для CSS со следующими свойствами:

  • В начале отсрочки нет, и переход не прекращается рано. В обоих направлениях (разворачивание и свертывание), если в CSS указать длительность перехода 300 мс, переход занимает 300 мс (точка).
  • Он изменяет фактическую высоту (в отличие от transform: scaleY(0)), поэтому он правильно работает, если после свертываемого элемента есть контент.
  • Хотя (как и в других решениях) есть являются магические числа (например, "выберите длину, которая больше, чем будет в вашем боксе"), это не смертельно, если ваше предположение окажется неверным. В этом случае переход может не выглядеть удивительно, но до и после перехода это не проблема: в расширенном состоянии (height: auto) все содержимое всегда имеет правильную высоту (в отличие, например, если вы выбираете max-height, который получается быть слишком низким). А в свернутом состоянии высота равна нулю, как и должно быть.

Демонстрация

Вот демонстрация с тремя складными элементами разной высоты, которые используют один и тот же CSS. Возможно, вы захотите нажать "Полная страница" после нажатия "Запустить фрагмент". Обратите внимание, что JavaScript включает только CSS-класс collapsed, измерение не требуется. (Вы можете сделать эту точную демонстрацию вообще без JavaScript, используя флажок или :target). Также обратите внимание, что часть CSS, которая отвечает за переход, довольно коротка, и HTML требует только одного дополнительного элемента-оболочки.

$(function () {
  $(".toggler").click(function () {
    $(this).next().toggleClass("collapsed");
    $(this).toggleClass("toggled"); // this just rotates the expander arrow
  });
});
.collapsible-wrapper {
  display: flex;
  overflow: hidden;
}
.collapsible-wrapper:after {
  content: '';
  height: 50px;
  transition: height 0.3s linear, max-height 0s 0.3s linear;
  max-height: 0px;
}
.collapsible {
  transition: margin-bottom 0.3s cubic-bezier(0, 0, 0, 1);
  margin-bottom: 0;
  max-height: 1000000px;
}
.collapsible-wrapper.collapsed > .collapsible {
  margin-bottom: -2000px;
  transition: margin-bottom 0.3s cubic-bezier(1, 0, 1, 1),
              visibility 0s 0.3s, max-height 0s 0.3s;
  visibility: hidden;
  max-height: 0;
}
.collapsible-wrapper.collapsed:after
{
  height: 0;
  transition: height 0.3s linear;
  max-height: 50px;
}

/* END of the collapsible implementation; the stuff below
   is just styling for this demo */

#container {
  display: flex;
  align-items: flex-start;
  max-width: 1000px;
  margin: 0 auto;
}  


.menu {
  border: 1px solid #ccc;
  box-shadow: 0 1px 3px rgba(0,0,0,0.5);
  margin: 20px;

  
}

.menu-item {
  display: block;
  background: linear-gradient(to bottom, #fff 0%,#eee 100%);
  margin: 0;
  padding: 1em;
  line-height: 1.3;
}
.collapsible .menu-item {
  border-left: 2px solid #888;
  border-right: 2px solid #888;
  background: linear-gradient(to bottom, #eee 0%,#ddd 100%);
}
.menu-item.toggler {
  background: linear-gradient(to bottom, #aaa 0%,#888 100%);
  color: white;
  cursor: pointer;
}
.menu-item.toggler:before {
  content: '';
  display: block;
  border-left: 8px solid white;
  border-top: 8px solid transparent;
  border-bottom: 8px solid transparent;
  width: 0;
  height: 0;
  float: right;
  transition: transform 0.3s ease-out;
}
.menu-item.toggler.toggled:before {
  transform: rotate(90deg);
}

body { font-family: sans-serif; font-size: 14px; }

*, *:after {
  box-sizing: border-box;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div id="container">
  <div class="menu">
    <div class="menu-item">Something involving a holodeck</div>
    <div class="menu-item">Send an away team</div>
    <div class="menu-item toggler">Advanced solutions</div>
    <div class="collapsible-wrapper collapsed">
      <div class="collapsible">
        <div class="menu-item">Separate saucer</div>
        <div class="menu-item">Send an away team that includes the captain (despite Riker's protest)</div>
        <div class="menu-item">Ask Worf</div>
        <div class="menu-item">Something involving Wesley, the 19th century, and a holodeck</div>
        <div class="menu-item">Ask Q for help</div>
      </div>
    </div>
    <div class="menu-item">Sweet-talk the alien aggressor</div>
    <div class="menu-item">Re-route power from auxiliary systems</div>
  </div>

  <div class="menu">
    <div class="menu-item">Something involving a holodeck</div>
    <div class="menu-item">Send an away team</div>
    <div class="menu-item toggler">Advanced solutions</div>
    <div class="collapsible-wrapper collapsed">
      <div class="collapsible">
        <div class="menu-item">Separate saucer</div>
        <div class="menu-item">Send an away team that includes the captain (despite Riker's protest)</div>
      </div>
    </div>
    <div class="menu-item">Sweet-talk the alien aggressor</div>
    <div class="menu-item">Re-route power from auxiliary systems</div>
  </div>

  <div class="menu">
    <div class="menu-item">Something involving a holodeck</div>
    <div class="menu-item">Send an away team</div>
    <div class="menu-item toggler">Advanced solutions</div>
    <div class="collapsible-wrapper collapsed">
      <div class="collapsible">
        <div class="menu-item">Separate saucer</div>
        <div class="menu-item">Send an away team that includes the captain (despite Riker's protest)</div>
        <div class="menu-item">Ask Worf</div>
        <div class="menu-item">Something involving Wesley, the 19th century, and a holodeck</div>
        <div class="menu-item">Ask Q for help</div>
        <div class="menu-item">Separate saucer</div>
        <div class="menu-item">Send an away team that includes the captain (despite Riker's protest)</div>
        <div class="menu-item">Ask Worf</div>
        <div class="menu-item">Something involving Wesley, the 19th century, and a holodeck</div>
        <div class="menu-item">Ask Q for help</div>
      </div>
    </div>
    <div class="menu-item">Sweet-talk the alien aggressor</div>
    <div class="menu-item">Re-route power from auxiliary systems</div>
  </div>

</div>

Как это работает?

На самом деле есть два переходов, вовлеченных в это. Один из них переводит margin-bottom из 0px (в расширенном состоянии) в -2000px в свернутом состоянии (аналогично этот ответ ). 2000 здесь - первое магическое число, оно основано на предположении, что ваша коробка не будет выше этой (2000 пикселей кажется разумным выбором).

Использование одного перехода margin-bottom само по себе имеет две проблемы:

  • Если у вас есть поле размером более 2000 пикселей, то margin-bottom: -2000px не будет скрывать все - будут видимые элементы даже в свернутом случае. Это небольшое исправление, которое мы сделаем позже.
  • Если фактическое поле имеет, скажем, высоту 1000 пикселей, а длина вашего перехода составляет 300 мс, то переход visible уже завершен примерно через 150 мс (или, в противоположном направлении, начинается с опозданием на 150 мс).

Исправление этой второй проблемы заключается в том, когда вступает второй переход, и этот переход концептуально нацелен на минимальный высоту оболочки ("концептуально", потому что мы фактически не используем для этого свойство min-height; подробнее об этом позже) ,.

Вот анимация, которая показывает, как объединение перехода нижнего поля с переходом минимальной высоты, одинаковой длительности, дает нам комбинированный переход от полной высоты к нулевой высоте, которая имеет одинаковую длительность.

animation as described above

Левая полоса показывает, как отрицательное нижнее поле сдвигает нижнюю часть вверх, уменьшая видимую высоту. Средняя полоса показывает, как минимальная высота гарантирует, что в случае свертывания переход не заканчивается досрочно, а в случае растяжения переход не начинается поздно. Правая полоса показывает, как комбинация этих двух элементов приводит к тому, что блок переходит от полной высоты к нулевой высоте за правильное время.

Для моей демонстрации я выбрал 50px в качестве верхнего минимального значения высоты. Это второе магическое число, и оно должно быть ниже, чем высота коробки. 50px кажется разумным; кажется маловероятным, что вы очень часто захотите сделать складной элемент, который не имеет даже 50 пикселей в первую очередь.

Как видно из анимации, результирующий переход является непрерывным, но он не дифференцируем - в момент, когда минимальная высота равна полной высоте, отрегулированной нижним полем, происходит внезапное изменение скорости. Это очень заметно в анимации, потому что она использует линейную функцию синхронизации для обоих переходов, и потому что весь переход очень медленный. В реальном случае (моя демонстрация вверху) переход занимает всего 300 мс, а переход нижнего поля не является линейным. Я поиграл с множеством различных функций синхронизации для обоих переходов, и те, с которыми я закончил, чувствовали, что они работают лучше всего для самых разных случаев.

Осталось решить две проблемы:

  1. точка сверху, где ящики высотой более 2000 пикселей не полностью скрыты в свернутом состоянии,
  2. и обратная проблема, когда в не скрытом случае поля высотой менее 50 пикселей слишком высоки, даже когда переход не выполняется, поскольку минимальная высота сохраняет их на уровне 50 пикселей.

Мы решаем первую проблему, предоставляя элементу контейнера max-height: 0 в свернутом случае с переходом 0s 0.3s. Это означает, что на самом деле это не переход, а max-height применяется с задержкой; он применяется только после завершения перехода. Чтобы это работало правильно, нам также нужно выбрать числовой max-height для противоположного, не свернутого состояния. Но в отличие от случая 2000px, где выбор слишком большого числа влияет на качество перехода, в этом случае это действительно не имеет значения. Таким образом, мы можем просто выбрать число, настолько высокое, что мы знаем, что никакая высота никогда не приблизится к этому. Я выбрал миллион пикселей. Если вам кажется, что вам может потребоваться поддержка контента высотой более миллиона пикселей, то 1) извините и 2) просто добавьте пару нулей.

Вторая проблема заключается в том, что мы фактически не используем min-height для перехода на минимальную высоту. Вместо этого в контейнере есть псевдоэлемент ::after с height, который переходит от 50px к нулю. Это имеет тот же эффект, что и min-height: он не позволит контейнеру сжиматься ниже любой высоты, которую имеет в настоящее время псевдоэлемент. Но поскольку мы используем height, а не min-height, теперь мы можем использовать max-height (еще раз примененный с задержкой), чтобы установить фактическую высоту псевдоэлемента равной нулю после завершения перехода, гарантируя, что по крайней мере за пределами перехода, даже небольшого элементы имеют правильную высоту. Поскольку min-heightболее сильный , чем 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, что делает это довольно бессмысленным упражнением.

72
balpha

Визуальное обходное решение для анимации высоты с использованием 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)

47
Catharsis

Мой обходной путь заключается в том, чтобы перевести max-height в точную высоту содержимого для плавной анимации Nice, а затем использовать обратный вызов 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>
46
Adam

Принятый ответ работает в большинстве случаев, но он не работает, когда 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>
35
Oleg Vaskevich

Используйте 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/

26
malihu

Нет жестко закодированных значений.

Нет 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 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>
26
Vivek Maharajh

Было мало упоминаний о свойстве Element.scrollHeight , которое может быть полезно здесь и все еще может использоваться с чистым переходом CSS. Свойство всегда содержит "полную" высоту элемента, независимо от того, переполняется ли его содержимое в результате свернутой высоты (например, height: 0).

Таким образом, для height: 0 (фактически полностью свернутого) элемента его "нормальная" или "полная" высота все еще легко доступна через его значение scrollHeight (неизменно пиксель длина).

Для такого элемента предполагается, что переход уже настроен, например, например, (используя ul согласно оригинальному вопросу):

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

Мы можем вызвать нужное анимированное "расширение" высоты, используя только CSS, с помощью чего-то вроде следующего (здесь предполагается, что переменная ul ссылается на список):

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

Вот и все. Если вам нужно свернуть список, подойдет любое из двух следующих утверждений:

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

Мой конкретный вариант использования вращался вокруг анимации списков неизвестной и часто значительной длины, поэтому мне было неудобно выбирать произвольную "достаточно большую" спецификацию height или max-height и рисковать обрезанным контентом или контентом, который вам внезапно нужно прокрутить (если overflow: auto, например). Кроме того, замедление и временные ограничения нарушаются решениями на основе max-height, поскольку используемая высота может достичь своего максимального значения намного раньше, чем потребуется max-height для достижения 9999px. И с ростом разрешения экрана, длина пикселей, как 9999px, оставляет неприятный вкус во рту. Это частное решение, на мой взгляд, решает проблему элегантно.

Наконец, здесь есть надежда, что будущие пересмотры авторов CSS-адресов должны делать такие вещи еще более элегантно - пересмотреть понятие "вычисленные" против "используемых" и "разрешенных" значений и рассмотреть, должны ли переходы применяться к вычисляемым значения, включая переходы с width и height (которые в настоящее время получают специальную обработку).

24
amn

Когда я публикую это, уже есть более 30 ответов, но я чувствую, что мой ответ улучшается на уже принятый ответ Джейк.

Я не был удовлетворен проблемой, которая возникает из-за простого использования переходов max-height и CSS3, поскольку, как отмечали многие комментаторы, вы должны установить значение max-height очень близко к фактической высоте, иначе вы получите задержку. Смотрите это JSFiddle для примера этой проблемы.

Чтобы обойти это (пока еще не используя JavaScript), я добавил еще один элемент HTML, который переводит значение CSS 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>
21
Andrew Messier

Итак, я думаю, что пришел с очень простым ответом ... нет 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; }

HTML

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

Правка: прокрутите вниз, чтобы получить обновленный ответ
Я делал выпадающий список и видел это сообщение ... много разных ответов, но я тоже решил поделиться своим выпадающим списком, ... Он не идеален, но по крайней мере он будет использовать только 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 установлен на содержимое, которое он может получить поверх содержимого вашего тела, поэтому я сделал это для ul:

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

и зависать:

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

второй раз после перехода задерживается, и он будет скрыт после того, как мой выпадающий список был закрыт анимацией ...
Надеюсь, позже кто-нибудь получит пользу от этого.

РЕДАКТИРОВАТЬ: Я просто не могу поверить, что на самом деле люди используют этот прототип! это выпадающее меню только для одного подменю и все !! Я обновил лучшее, которое может иметь два подменю для направления ltr и rtl с поддержкой IE 8.
скрипка для LTR
Fiddle for RTL
надеюсь, кто-то найдет это полезным в будущем.

14
Sijav

Вы можете перейти с высоты: 0 на высоту: автоматически, при условии, что вы также указали минимальную высоту и максимальную высоту.

div.stretchy{
    transition: 1s linear;
}

div.stretchy.hidden{
    height: 0;
}

div.stretchy.visible{
    height: auto;
    min-height:40px;
    max-height:400px;
}
11
Stuart Badminton

Решение Flexbox

Плюсы:

  • просто
  • нет JS
  • плавный переход

Минусы:

  • элемент должен быть помещен в гибкий контейнер фиксированной высоты

Он работает так, что всегда имеет flex-based: auto для элемента с содержимым, а вместо этого переходят flex-grow и flex-shrink.

Редактирование: улучшен 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 Fiddle

10
Lee Comstock

Я думаю, что я нашел действительно твердое решение

ХОРОШО! Я знаю, что эта проблема такая же старая, как Интернет, но я думаю, что у меня есть решение, которое я превратил в плагин с именем mutant-transition . Мое решение устанавливает атрибуты style="" для отслеживаемых элементов всякий раз, когда происходит изменение в DOM. конечный результат заключается в том, что вы можете использовать хороший оле CSS для ваших переходов и не использовать хакерские исправления или специальный javascript. Единственное, что вам нужно сделать, это установить то, что вы хотите отслеживать для данного элемента, используя data-mutant-attributes="X".

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

Это оно! Это решение использует MutationObserver для отслеживания изменений в DOM. Из-за этого вам не нужно ничего настраивать или использовать javascript для ручной анимации. Изменения отслеживаются автоматически. Однако, поскольку он использует MutationObserver, он будет переходить только в IE11 +.

скрипки!

9
JonTroncoso

Вот способ перехода с любой начальной высоты, включая 0, на автоматический (полноразмерный и гибкий), не требующий жесткого набора кода для каждого узла или любого пользовательского кода для инициализации: https: // github .com/csuwildcat/переход-авто . Я считаю, что это в основном святой Грааль для того, что вы хотите -> 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-файл на свою страницу - все это просто работает ™

/ * Код для высоты: авто; переход * /

(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);
7
csuwldcat

Ответ Джейка на анимацию максимальной высоты великолепен, но задержка, вызванная установкой большой максимальной высоты, меня раздражает.

Можно переместить сворачиваемое содержимое во внутренний div и вычислить максимальную высоту, получив высоту внутреннего div (через JQuery это будет externalHeight ()).

$('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/

7
pbatey

Я понимаю, что эта ветка стареет, но в некоторых поисках Google она занимает высокое место, поэтому я считаю, что ее стоит обновить.

Вы также просто получаете/устанавливаете собственную высоту элемента:

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

Вы должны выбросить этот Javascript сразу после закрывающего тега target_box во встроенном теге скрипта.

5
Charley

Расширяя ответ @ jake, переход будет проходить вплоть до максимального значения высоты, вызывая очень быструю анимацию - если вы установите переходы для обоих: hover и off, вы можете затем контролировать сумасшедшую скорость немного больше.

Таким образом, li: hover - это когда мышь входит в состояние, а затем переходом свойства non-hovered будет отпуск мыши.

Надеюсь, это поможет.

например:

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

5
jamie

Вот решение, которое я только что использовал в сочетании с 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 }
4
j-man86

Я был в состоянии сделать это. У меня есть .child и .parent div. Дочерний div идеально вписывается в ширину/высоту родителя с позицией absolute. Затем я анимирую свойство translate, чтобы Push его значение 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 */
}

Вы должны указать height в .parent, в px, % или оставить как auto. Этот div затем маскирует div .child, когда он скользит вниз.

4
Josh Ribakoff

Недавно я перевел max-height на элементы li, а не на обтекание ul.

Причина заключается в том, что задержка для небольшого max-heights гораздо менее заметна (если вообще) по сравнению с большим max-heights, и я также могу установить свое значение max-height относительно font-sizeli, а не произвольное огромное число, используя ems или rems.

Если мой размер шрифта равен 1rem, я установлю свой max-height на что-то вроде 3rem (для размещения обернутого текста). Вы можете увидеть пример здесь:

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

4
mindfullsilence

Я понимаю, что вопрос требует решения без 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>
3
shunryu111

Я не прочитал все подробно, но у меня недавно была эта проблема, и я сделал следующее:

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 - это предел, который я навязываю. Убедитесь, что у вас есть переход на non: hover, иначе вы можете получить странный рендеринг. Таким образом,: hover наследуется от non: hover.

Переход не работает с px на% или на auto. Вам нужно использовать ту же единицу измерения. Это прекрасно работает для меня. Использование HTML5 делает его идеальным ....

Помните, что всегда есть работа вокруг ...; )

Надеюсь, кто-то найдет это полезным

3
work.stella84

Это не совсем "решение" проблемы, а скорее обходной путь. Я уверен, что он работает только так, как написано с текстом, но его можно изменить для работы с другими элементами.

.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

По сути, вы устанавливаете размер шрифта равным 0 и переводите его вместо высоты, или max-height, или scaleY () и т.д. В достаточно быстром темпе, чтобы высота трансформировалась в то, что вы хотите. Преобразовать фактическую высоту с помощью CSS в auto в настоящее время невозможно, но преобразование содержимого внутри, следовательно, переход размера шрифта.

  • Примечание - в коде ручки есть IS javascript, но его единственная цель - добавлять/удалять классы css по клику для аккордеона. Это можно сделать с помощью скрытых переключателей, но я не сосредоточился на этом, только на изменении высоты.
3
Mike S.

Кажется, нет правильного решения. Подход max-height довольно хорош, но не подходит для фазы скрытия - будет заметная задержка, если вы не знаете высоту содержимого.

Я думаю, что лучший способ - это использовать max-height но только для фазы показа. И не использовать никакой анимации при сокрытии. Для большинства случаев это не должно быть решающим.

max-height должен быть установлен в довольно большое значение, чтобы обеспечить любой контент подходит. Скорость анимации можно контролировать с помощью transition duration (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>
2
Andrej

Решение по максимальной высоте от Джейка хорошо работает, если жестко заданное значение максимальной высоты не намного превышает реальную высоту (поскольку в противном случае возникают нежелательные задержки и проблемы с синхронизацией). С другой стороны, если жестко закодированное значение случайно не превышает реальную высоту, элемент не откроется полностью.

Следующее решение только для CSS также требует жестко заданного размера, который должен быть больше, чем большинство существующих реальных размеров. Однако это решение также работает, если реальный размер в некоторых ситуациях превышает жестко заданный размер. В этом случае переход может немного подскочить, но он никогда не оставит частично видимый элемент. Таким образом, это решение также может быть использовано для неизвестного контента, например, из базы данных, где вы просто знаете, что контент обычно не больше, чем х пикселей, но есть исключения.

Идея состоит в том, чтобы использовать отрицательное значение для margin-bottom (или margin-top для слегка отличающейся анимации) и поместить элемент содержимого в средний элемент с переполнением: hidden. Отрицательное поле элемента содержимого уменьшает высоту среднего элемента.

В следующем коде используется переход по полям с -150px на 0px. Это само по себе работает нормально, если элемент содержимого не превышает 150 пикселей. Кроме того, он использует переход на max-height для среднего элемента от 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%
}

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>

Значение поля margin должно быть отрицательным и максимально приближенным к реальной высоте элемента содержимого. Если оно (абсолютное значение) больше, то возникают такие же проблемы с задержкой и синхронизацией, как и в решениях с максимальной высотой, которые, однако, могут быть ограничены, если размер в жестком коде не намного больше, чем реальный. Если абсолютное значение для margin-bottom меньше, чем реальная высота, переход немного увеличивается. В любом случае после перехода элемент содержимого либо полностью отображается, либо полностью удаляется.

Для получения дополнительной информации см. Мой пост в блоге http://www.taccgl.org/blog/css_transition_display.html#combined_height

2
Helmut Emmelmann

Установите высоту на авто и измените максимальную высоту.

Протестировано на 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%; /**/
}
1
will Farrell

МАЛЕНЬКИЙ JAVASCRIPT + РЕШЕНИЕ SCSS

Я обычно использую совершенно другую точку зрения и (очень) маленький JavaScript. Дело в том:

  • что мы действительно хотим, это изменить высоту

  • Высота - это сумма всех элементов списка внутри подменю.

  • Мы обычно знаем высоту элемента списка, так как мы его стилизуем

Таким образом, мое решение применимо к "нормальным" подменю, где имена элементов имеют только 1 строку. В любом случае, с чуть большим количеством js можно разместить даже более 1 имени строки.

По сути, я просто считаю элементы подменю и применяю определенные классы соответственно. Затем передать мяч (ы) CSS. Так, например:

var main_menu = $('.main-menu');
var submenus = $('.main-menu').find('.submenu');
submenus.each(function(index,item){
   var i = $(item);
   i.addClass('has-' + i.find('li').length + '-children');
});

Вы можете использовать любой класс/селектор, очевидно. На данный момент у нас есть такие подменю:

<ul class="submenu has-3-children">
   <li></li>
   <li></li>
   <li></li>
</ul>

И наш CSS так:

.submenu{
   //your styles [...]
   height:0;
   overflow:hidden;
   transition: all 200ms ease-in-out; //assume Autoprefixer is used
}

У нас также будут некоторые переменные scss, подобные этим (произвольный пример):

$sub_item_height:30px;
$sub_item_border:2px;

На данный момент, если предположить, что открытые пункты главного меню получат класс типа "открытый" или тому подобное (ваши реализации ..), мы можем сделать что-то вроде этого:

//use a number of children reasonably high so it won't be overcomed by real buttons
.main-menu .opened .submenu{
   &.has-1-children{ height:   $sub_item_height*1  + $sub_item_border*1;  }
   &.has-2-children{ height:   $sub_item_height*2  + $sub_item_border*2;  }
   &.has-3-children{ height:   $sub_item_height*3  + $sub_item_border*3;  }
   //and so on....
}

Или, чтобы сократить

.main-menu .opened .submenu{
   @for $i from 1 through 12{//12 is totally arbitrary
      &.has-#{$i}-children { height: $menu_item_height * $i + $menu_item_border * $i; }
   }
}

В большинстве случаев это сделает работу. Надеюсь, поможет!

0
Luca Reghellin

Это обычная проблема, которую я решил вот так

http://jsfiddle.net/ipeshev/d1dfr0jz/

Попробуйте установить задержку в закрытом состоянии на некоторое отрицательное число и поиграть немного со значением. Вы увидите разницу. Его можно заставить почти соврать человеческий глаз;).

Он работает в основных браузерах, но достаточно хорош для меня. Странно, но дают некоторые результаты.

.expandable {
    max-height: 0px;
    overflow: hidden;
    transition: all 1s linear -0.8s;
}

button:hover ~ .expandable {
    max-height: 9000px;
    transition: all 1s ease-in-out;
}
0
Ivan Peshev