Я пытаюсь заставить <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>
32 ответа
Используйте в переходе 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>
В настоящее время вы не можете анимировать по высоте, когда одна из задействованных высот равна auto
, вы должны установить две явные высоты.
Решение, которое я всегда использовал, заключалось в том, чтобы сначала постепенно исчезнуть, а затем сжать значения font-size
, padding
и margin
. Это не похоже на вайп, но работает без статических height
или max-height
.
Рабочий пример:
/* final display */
#menu #list {
margin: .5em 1em;
padding: 1em;
}
/* hide */
#menu:not(:hover) #list {
font-size: 0;
margin: 0;
opacity: 0;
padding: 0;
/* fade out, then shrink */
transition: opacity .25s,
font-size .5s .25s,
margin .5s .25s,
padding .5s .25s;
}
/* reveal */
#menu:hover #list {
/* unshrink, then fade in */
transition: font-size .25s,
margin .25s,
padding .25s,
opacity .5s .25s;
}
<div id="menu">
<b>hover me</b>
<ul id="list">
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
</ul>
</div>
<p>Another paragraph...</p>
*
и применив другие изменения. Однако на кнопки должен был повлиять мой приведенный выше код, если бы они были правильно стилизованы с помощью em
.
Это решение только для CSS со следующими свойствами:
- В начале нет задержки, и переход не прекращается рано. В обоих направлениях (раскрытие и сворачивание), если вы укажете в 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
; подробнее об этом позже).
Вот анимация, которая показывает, как объединение перехода нижнего поля с переходом минимальной высоты, оба имеют равную продолжительность, дает нам комбинированный переход от полной высоты к нулевой высоте с такой же продолжительностью.
Левая полоса показывает, как отрицательное нижнее поле подталкивает нижнюю часть вверх, уменьшая видимую высоту. Средняя полоса показывает, как минимальная высота гарантирует, что в случае сворачивания переход не завершится раньше, а в случае расширения переход не начнется поздно. Правая полоса показывает, как комбинация этих двух параметров заставляет коробку переходить с полной высоты на нулевую за правильный промежуток времени.
Для моей демонстрации я выбрал 50 пикселей в качестве верхнего минимального значения высоты. Это второе магическое число, и оно должно быть меньше, чем когда-либо была бы высота коробки. 50 пикселей тоже кажется разумным; кажется маловероятным, что вы очень часто захотите сделать элемент сворачиваемым, если его высота не превышает 50 пикселей.
Как вы можете видеть на анимации, результирующий переход является непрерывным, но не дифференцируемым - в момент, когда минимальная высота равна полной высоте, регулируемой нижним полем, происходит резкое изменение скорости. Это очень заметно в анимации, потому что она использует линейную функцию синхронизации для обоих переходов, а также потому, что весь переход очень медленный. В реальном случае (моя демонстрация вверху) переход занимает всего 300 мс, а переход нижнего поля не является линейным. Я экспериментировал с множеством различных функций синхронизации для обоих переходов, и те, которые у меня были, мне показалось, что они лучше всего подходят для самых разных случаев.
Осталось исправить две проблемы:
- точка сверху, где блоки высотой более 2000 пикселей не полностью скрыты в свернутом состоянии,
- и обратная проблема, когда в не скрытом случае блоки высотой менее 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, что делает это довольно бессмысленным упражнением.
Можно, с небольшой долей несемантической уловки. Мой обычный подход - анимировать высоту внешнего 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
.
Визуальный обходной путь для анимации высоты с использованием переходов 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 )
Принятый ответ работает в большинстве случаев, но он не работает, когда ваш 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>
Мое обходное решение - перейти max-height
к точной высоте содержимого для приятной плавной анимации, а затем использовать обратный вызов transitionEnd
, чтобы установить для max-height
значение 9999px
, чтобы можно было свободно изменять размер содержимого.
var content = $('#content');
content.inner = $('#content .inner'); // inner div needed to get size of content when closed
// css transition callback
content.on('transitionEnd webkitTransitionEnd transitionend oTransitionEnd msTransitionEnd', function(e){
if(content.hasClass('open')){
content.css('max-height', 9999); // try setting this to 'none'... I dare you!
}
});
$('#toggle').on('click', function(e){
content.toggleClass('open closed');
content.contentHeight = content.outerHeight();
if(content.hasClass('closed')){
// disable transitions & set max-height to content height
content.removeClass('transitions').css('max-height', content.contentHeight);
setTimeout(function(){
// enable & start transition
content.addClass('transitions').css({
'max-height': 0,
'opacity': 0
});
}, 10); // 10ms timeout is the secret ingredient for disabling/enabling transitions
// chrome only needs 1ms but FF needs ~10ms or it chokes on the first animation for some reason
}else if(content.hasClass('open')){
content.contentHeight += content.inner.outerHeight(); // if closed, add inner height to content height
content.css({
'max-height': content.contentHeight,
'opacity': 1
});
}
});
.transitions {
transition: all 0.5s ease-in-out;
-webkit-transition: all 0.5s ease-in-out;
-moz-transition: all 0.5s ease-in-out;
}
body {
font-family:Arial;
line-height: 3ex;
}
code {
display: inline-block;
background: #fafafa;
padding: 0 1ex;
}
#toggle {
display:block;
padding:10px;
margin:10px auto;
text-align:center;
width:30ex;
}
#content {
overflow:hidden;
margin:10px;
border:1px solid #666;
background:#efefef;
opacity:1;
}
#content .inner {
padding:10px;
overflow:auto;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<div id="content" class="open">
<div class="inner">
<h3>Smooth CSS Transitions Between <code>height: 0</code> and <code>height: auto</code></h3>
<p>A clever workaround is to use <code>max-height</code> instead of <code>height</code>, and set it to something bigger than your content. Problem is the browser uses this value to calculate transition duration. So if you set it to <code>max-height: 1000px</code> but the content is only 100px high, the animation will be 10x too fast.</p>
<p>Another option is to measure the content height with JS and transition to that fixed value, but then you have to keep track of the content and manually resize it if it changes.</p>
<p>This solution is a hybrid of the two - transition to the measured content height, then set it to <code>max-height: 9999px</code> after the transition for fluid content sizing.</p>
</div>
</div>
<br />
<button id="toggle">Challenge Accepted!</button>
Было мало упоминания о 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
(которые в настоящее время подвергаются особой обработке).
Element.scrollHeight
требуется JavaScript, и, как ясно сказано в вопросе, они хотят сделать это без JavaScript.
Нет жестко закодированных значений.
Нет 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>
На момент публикации я уже получил более 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>
Используйте max-height
с разной скоростью перехода и задержкой для каждого состояния.
HTML:
<a href="#" id="trigger">Hover</a>
<ul id="toggled">
<li>One</li>
<li>Two</li>
<li>Three</li>
<ul>
CSS:
#toggled{
max-height: 0px;
transition: max-height .8s cubic-bezier(0, 1, 0, 1) -.1s;
}
#trigger:hover + #toggled{
max-height: 9999px;
transition-timing-function: cubic-bezier(0.5, 0, 1, 0);
transition-delay: 0s;
}
См. Пример: http://jsfiddle.net/0hnjehjc/1/
999999px
. Это, с другой стороны, сместит вашу анимацию, потому что кадры рассчитываются на основе этого значения. Таким образом, вы можете получить "задержку" анимации в одном направлении и очень быстрое завершение в другом направлении (corr: time-functions)
Хорошо, думаю, я придумал очень простой ответ ... нет 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>
РЕДАКТИРОВАТЬ: прокрутите вниз, чтобы увидеть обновленный ответ
Я составлял раскрывающийся список и видел этот пост ... много разных ответов, но я решил также поделиться своим раскрывающимся списком ... Он не идеален, но, по крайней мере, он будет использовать только 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
надеюсь, кому-то это пригодится в будущем.
Вы можете перейти от height: 0 к height: auto при условии, что вы также укажете min-height и max-height.
div.stretchy{
transition: 1s linear;
}
div.stretchy.hidden{
height: 0;
}
div.stretchy.visible{
height: auto;
min-height:40px;
max-height:400px;
}
height
, только max-height
. height
мгновенно перейдет с 0
на auto
, а max-height
выполнит переход, который кажется, работает , но создает ту же проблему, что и другие ответы. адресовать.
Вот способ перехода от любой начальной высоты, включая 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);
Решение 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>
Вот решение, которое я только что использовал в сочетании с 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 }
В этом решении используется несколько приемов:
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>
Решение, состоящее из одного предложения: Использовать переход с отступом . Этого достаточно для большинства случаев, таких как аккордеон, и даже лучше, потому что это быстро из-за того, что значение заполнения часто невелико.
Если вы хотите, чтобы процесс анимации был лучше, просто увеличьте значение отступа.
.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>
Думаю, я нашел действительно надежное решение
ОК! Я знаю, что эта проблема стара как Интернет, но я думаю, что у меня есть решение, которое я превратил в плагин под названием 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 +.
Скрипки!
- Демонстрация перехода с
height: auto
наheight: 100%
- Демонстрация перехода
height: auto
при добавлении детей
Ответ Джейка на анимацию максимальной высоты великолепен, но я обнаружил, что задержка, вызванная установкой большой максимальной высоты, раздражает.
Можно переместить сворачиваемое содержимое во внутренний 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/
Я понимаю, что эта ветка устаревает, но она занимает высокие позиции в определенных поисковых запросах Google, поэтому я считаю, что ее стоит обновить.
Вы также просто получаете / устанавливаете собственную высоту элемента:
var load_height = document.getElementById('target_box').clientHeight;
document.getElementById('target_box').style.height = load_height + 'px';
Вы должны сбросить этот Javascript сразу после закрывающего тега target_box во встроенном теге сценария.
Недавно я перенес 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
Я понимаю, что вопрос требует решения без 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>
Я не читал все подробно, но недавно у меня возникла эта проблема, и я сделал следующее:
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.
Не работает переход с пикселей на% или на авто. Вам нужно использовать ту же единицу измерения.
Я смог это сделать. У меня есть 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
, когда он скользит вниз.
Вместо этого вы должны использовать 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 вместо высоты.
Изменить Некоторым людям не нравится, как scaleY
преобразует контент. Если это проблема, я предлагаю вместо этого использовать clip
.
ul {
clip: rect(auto, auto, 0, auto);
position: absolute;
margin: -1rem 0;
padding: .5rem;
color: white;
background-color: rgba(0, 0, 0, 0.8);
transition-property: clip;
transition-duration: 0.5s;
transition-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
h3:hover ~ ul,
h3:active ~ ul,
ul:hover {
clip: rect(auto, auto, 10rem, auto);
}
<h3>Hover here</h3>
<ul>
<li>This list</li>
<li>is clipped.</li>
<li>A clip transition</li>
<li>will show it</li>
</ul>
<p>
Some text...
</p>
clip
ничем не отличается от простой установки значения max-height
. Он по-прежнему страдает от нефиксированной задержки видимой анимации с очень динамичным размером содержимого.
Вы можете сделать это, создав обратную (свернутую) анимацию с помощью 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>
Расширяя ответ @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/
Согласно MDN Web Docs, auto
значения были намеренно исключены из спецификации переходов CSS, поэтому вместо height: auto
используйте height: 100%
, top code> или свойство
flex
в макетах сетки и гибкости.
Развертывание / сворачивание наложения
.grid-container {
display: grid;
position: absolute;
}
.content {
background: aqua;
height: 0;
overflow: hidden;
transition: all 1s ease;
}
span:hover + .grid-container .content {
height: 100%;
}
<span>Hover over me!</span>
<div class="grid-container">
<div class="content">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</div>
</div>
<p>Rest of the page content...</p>
Развертывание / сворачивание скользящего наложения
.grid-container {
display: grid;
position: absolute;
overflow: hidden;
pointer-events: none; /* to enable interaction with elements below the container */
}
.content {
background: aqua;
pointer-events: auto;
position: relative;
top: -100%;
transition: all 1s ease;
}
span:hover + .grid-container .content {
top: 0;
}
<span>Hover over me!</span>
<div class="grid-container">
<div class="content">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</div>
</div>
<p>Rest of the page content...</p>
Разворачивание / сворачивание в документообороте
html {
display: grid;
}
body {
display: flex;
flex-direction: column;
}
.content {
background: aqua;
flex-basis: 0;
overflow: hidden;
transition: all 1s ease;
}
span:hover + .content {
flex: 1;
}
<span>Hover over me!</span>
<div class="content">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</div>
<p>Rest of the page content...</p>
Expanding/collapsing in the document flow
, но он не работает, поскольку контейнер имеет определенную высоту, поэтому будет затронут остальной контент, контент вне контейнера,
Альтернативное решение только для CSS с line-height
, padding
, opacity
и margin
:
body {
background-color: linen;
}
main {
background-color: white;
}
[id^="toggle_"] ~ .content {
line-height: 0;
opacity: 0;
padding: 0 .5rem;
transition: .2s ease-out;
}
[id^="toggle_"] ~ .content > p {
margin: 0;
transition: .2s ease-out;
}
[id^="toggle_"]:checked ~ .content {
opacity: 1;
padding: .5rem;
line-height: 1.5;
}
[id^="toggle_"]:checked ~ .content p {
margin-bottom: .75rem;
}
[id^="toggle_"] + label {
display: flex;
justify-content: space-between;
padding: 0.5em 1em;
background: lightsteelblue;
border-bottom: 1px solid gray;
cursor: pointer;
}
[id^="toggle_"] + label:before {
content: "Show";
}
[id^="toggle_"]:checked + label:before {
content: "Hide";
}
[id^="toggle_"] + label:after {
content: "\25BC";
}
[id^="toggle_"]:checked + label:after {
content: "\25B2";
}
<main>
<div>
<input type="checkbox" id="toggle_1" hidden>
<label for="toggle_1" hidden></label>
<div class="content">
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis dolor neque, commodo quis leo ut, auctor tincidunt mauris. Nunc fringilla tincidunt metus, non gravida lorem condimentum non. Duis ornare purus nisl, at porta arcu eleifend eget. Integer lorem ante, porta vulputate dui ut, blandit tempor tellus. Proin facilisis bibendum diam, sit amet rutrum est feugiat ut. Mauris rhoncus convallis arcu in condimentum. Donec volutpat dui eu mollis vulputate. Nunc commodo lobortis nunc at ultrices. Suspendisse in lobortis diam. Suspendisse eget vestibulum ex.
</p>
</div>
</div>
<div>
<input type="checkbox" id="toggle_2" hidden>
<label for="toggle_2" hidden></label>
<div class="content">
<p>
Maecenas laoreet nunc sit amet nulla ultrices auctor. Vivamus sed nisi vitae nibh condimentum pulvinar eu vel lorem. Sed pretium viverra eros ut facilisis. In ut fringilla magna. Sed a tempor libero. Donec sapien libero, lacinia sed aliquet ut, imperdiet finibus tellus. Nunc tellus lectus, rhoncus in posuere quis, tempus sit amet enim. Morbi et erat ac velit fringilla dignissim. Donec commodo, est id accumsan cursus, diam dui hendrerit nisi, vel hendrerit purus dolor ut risus. Phasellus mattis egestas ipsum sed ullamcorper. In diam ligula, rhoncus vel enim et, imperdiet porta justo. Curabitur vulputate hendrerit nisl, et ultricies diam. Maecenas ac leo a diam cursus ornare nec eu quam.
</p>
<p>Sed non vulputate purus, sed consectetur odio. Sed non nibh fringilla, imperdiet odio nec, efficitur ex. Suspendisse ut dignissim enim. Maecenas felis augue, tempor sit amet sem fringilla, accumsan fringilla nibh. Quisque posuere lacus tortor, quis malesuada magna elementum a. Nullam id purus in ante molestie tincidunt. Morbi luctus orci eu egestas dignissim. Sed tincidunt, libero quis scelerisque bibendum, ligula nisi gravida libero, id lacinia nulla leo in elit.
</p>
<p>Aenean aliquam risus id consectetur sagittis. Aliquam aliquam nisl eu augue accumsan, vel maximus lorem viverra. Aliquam ipsum dolor, tempor et justo ac, fermentum mattis dui. Etiam at posuere ligula. Vestibulum tortor metus, viverra vitae mi non, laoreet iaculis purus. Praesent vel semper nibh. Curabitur a congue lacus. In et pellentesque lorem. Morbi posuere felis non diam vulputate, non vulputate ex vehicula. Vivamus ultricies, massa id sagittis consequat, sem mauris tincidunt nunc, eu vehicula augue quam ut mauris.
</p>
</div>
</div>
<div>
<input type="checkbox" id="toggle_3" hidden>
<label for="toggle_3" hidden></label>
<div class="content">
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis dolor neque, commodo quis leo ut, auctor tincidunt mauris. Nunc fringilla tincidunt metus, non gravida lorem condimentum non. Duis ornare purus nisl, at porta arcu eleifend eget. Integer lorem ante, porta vulputate dui ut, blandit tempor tellus. Proin facilisis bibendum diam, sit amet rutrum est feugiat ut. Mauris rhoncus convallis arcu in condimentum. Donec volutpat dui eu mollis vulputate. Nunc commodo lobortis nunc at ultrices. Suspendisse in lobortis diam. Suspendisse eget vestibulum ex.
</p>
<p>Sed non vulputate purus, sed consectetur odio. Sed non nibh fringilla, imperdiet odio nec, efficitur ex. Suspendisse ut dignissim enim. Maecenas felis augue, tempor sit amet sem fringilla, accumsan fringilla nibh. Quisque posuere lacus tortor, quis malesuada magna elementum a. Nullam id purus in ante molestie tincidunt. Morbi luctus orci eu egestas dignissim. Sed tincidunt, libero quis scelerisque bibendum, ligula nisi gravida libero, id lacinia nulla leo in elit.
</p>
</div>
</div>
</main>
Похожие вопросы
Новые вопросы
css
CSS (каскадные таблицы стилей) - это язык таблиц стилей представления, используемый для описания внешнего вида и форматирования документов HTML (язык разметки гипертекста), XML (расширяемый язык разметки) и элементов SVG, включая (но не ограничиваясь) цвета, макет, шрифты, и анимации. Также описывается, как элементы должны отображаться на экране, на бумаге, в речи или на других носителях.
height:auto/max-height
будет работать, только если вы расширяете область больше, чемheight
, которую вы хотите ограничить. Если у вас естьmax-height
из300px
, но раскрывающийся список со списком, который может возвращать50px
, тоmax-height
вам не поможет,50px
является переменной в зависимости от количества элементов, вы можете попасть в невозможную ситуацию, когда я не могу это исправить, потому чтоheight
не исправлен,height:auto
был решением, но я не могу использовать переходы с этим .