Я пытаюсь заставить <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>
2500
Hailwood 18 Авг 2010 в 06:50

30 ответов

Лучший ответ

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

См. демонстрацию 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>
3083
jlouzado 16 Окт 2019 в 19:21

Вместо этого вы должны использовать 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 вместо высоты.

371
A. Meshu 31 Дек 2019 в 20:30

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

231
animuson 25 Фев 2015 в 15:53

Решение, которое я всегда использовал, заключалось в том, чтобы сначала постепенно исчезнуть, а затем сжать значения 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>
148
Samuel Liew 29 Окт 2018 в 22:37

Я знаю, что это тридцать с чем-то ответом на этот вопрос, но я думаю, что оно того стоит, так что приступим. Это решение только для 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 из 0 пикселей (в развернутом состоянии) в -2000px в свернутом состоянии (аналогично этот ответ). 2000 здесь - первое магическое число, оно основано на предположении, что ваша коробка не будет выше этого (2000 пикселей кажутся разумным выбором).

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

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

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

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

animation as described above

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

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

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

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

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

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

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

Протестировано в Chrome (Win, Mac, Android, iOS), Firefox (Win, Mac, Android), Edge, IE11 (за исключением проблемы с макетом flexbox в моей демонстрации, отладкой которой я не занимался) и Safari (Mac, iOS ). Говоря о flexbox, должно быть возможно сделать эту работу без использования flexbox; на самом деле я думаю, что вы могли бы заставить работать почти все в IE7 - за исключением того факта, что у вас не будет переходов CSS, что делает это довольно бессмысленным упражнением.

109
Community 23 Май 2017 в 11:55

Можно, с небольшой долей несемантической уловки. Мой обычный подход - анимировать высоту внешнего 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.

93
Spencer O'Reilly 11 Дек 2017 в 18:38

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

55
Joran Den Houting 31 Окт 2013 в 10:12

Мое обходное решение - перейти от max-height к точной высоте содержимого для приятной плавной анимации, а затем использовать обратный вызов transitionEnd, чтобы установить max-height на 9999 пикселей, чтобы содержимое можно было свободно изменять.

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>
47
animuson 25 Фев 2015 в 15:49

Было мало упоминания о Element.prototype.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 (которые в настоящее время подвергаются особой обработке).

36
amn 24 Сен 2020 в 09:24

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

Нет 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>
34
Vivek Maharajh 26 Ноя 2017 в 07:02

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

32
malihu 16 Дек 2014 в 23:35

На момент публикации я уже получил более 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>
22
Community 23 Май 2017 в 12:10

Хорошо, думаю, я придумал очень простой ответ ... нет 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>
18
VIDesignz 1 Авг 2015 в 22:35

РЕДАКТИРОВАТЬ: прокрутите вниз, чтобы увидеть обновленный ответ
Я составлял раскрывающийся список и видел этот пост ... много разных ответов, но я решил также поделиться своим раскрывающимся списком ... Он не идеален, но, по крайней мере, он будет использовать только 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.
Fiddle для LTR
Fiddle для RTL
надеюсь, кому-то это пригодится в будущем.

16
Sijav 7 Ноя 2018 в 23:26

Вы можете перейти от 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;
}
11
Joran Den Houting 31 Окт 2013 в 10:03

Расширяя ответ @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/

8
Joran Den Houting 31 Окт 2013 в 10:36

Решение Flexbox

Плюсы:

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

Минусы:

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

Это работает так: всегда есть flex-base: 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

8
Lee Comstock 3 Май 2018 в 20:52

Вот способ перехода от любой начальной высоты, включая 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-файл на свою страницу - все работает ™

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

(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);
6
csuwldcat 18 Сен 2014 в 18:35

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

ОК! Я знаю, что эта проблема стара как Интернет, но я думаю, что у меня есть решение, которое я превратил в плагин под названием mutant -переход. Мое решение устанавливает атрибуты 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 +.

Скрипки!

6
JonTroncoso 19 Май 2015 в 07:47

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

Можно переместить сворачиваемое содержимое во внутренний 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/

6
user3336882 5 Авг 2015 в 12:13

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

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

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

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

5
Charley 27 Май 2011 в 01:13

Я смог это сделать. У меня есть div .child и .parent. Дочерний div идеально вписывается в ширину / высоту родительского элемента с позиционированием absolute. Затем я анимирую свойство translate, чтобы уменьшить его Y значение 100%. Его очень плавная анимация, без сбоев и недостатков, как у любого другого решения.

Примерно так, псевдокод

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

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

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

5
Jonatas Walker 23 Ноя 2015 в 08:31

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

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

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

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

5
Jonatas Walker 23 Ноя 2015 в 08:33

Я отправил ответ с помощью JavaScript и получил отрицательное голосование, поэтому разозлился и попытался снова, и взломал его только с помощью CSS!

В этом решении используется несколько "приемов":

  • padding-bottom:100% "взломать", где проценты определяются в терминах текущей ширины элемента. Дополнительная информация об этом методе.
  • термоусадочная упаковка с плавающей запятой (требуется дополнительный div для применения хака очистки float)
  • несемантическое использование https://caniuse.com/#feat=css-writing-mode и некоторые преобразования, чтобы отменить это (это позволяет использовать хак с заполнением выше в вертикальном контексте)

В результате мы получаем эффективный переход с использованием только CSS и единственную функцию перехода для плавного перехода; Святой Грааль!

Конечно, есть и обратная сторона! Я не могу понять, как контролировать ширину обрезки контента (overflow:hidden); из-за хака padding-bottom ширина и высота тесно связаны. Хотя может быть способ, так что вернемся к нему.

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>
5
EoghanM 12 Ноя 2019 в 10:54

Решение, состоящее из одного предложения: Использовать переход с отступом . Этого достаточно для большинства случаев, таких как аккордеон, и даже лучше, потому что это быстро из-за того, что значение заполнения часто невелико.

Если вы хотите, чтобы процесс анимации был лучше, просто увеличьте значение отступа.

.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>
5
dallaslu 15 Апр 2020 в 05:44

Вы можете сделать это, создав обратную (свернутую) анимацию с помощью 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>
4
TylerH 24 Фев 2020 в 14:22

Я понимаю, что вопрос требует решения без 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>
4
shunryu111 6 Апр 2021 в 10:41

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

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

Не работает переход с пикселей на% или на авто. Вам нужно использовать ту же единицу измерения.

4
TylerH 28 Апр 2021 в 17:52

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

Вот решение, которое я только что использовал в сочетании с 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
HandiworkNYC.com 2 Ноя 2011 в 23:58