Ниже приведен упрощенный (и преувеличенный) пример аккордеона, который использует сетку для изменения высоты содержимого.
Переход работает, но в Chrome ведет себя странно: контейнер (.accordion
) начинает расти быстрее, чем содержимое, и содержимому требуется некоторое время, чтобы наверстать упущенное по высоте, поэтому во время перехода в нижней части контейнера появляется пробел (см. скриншот).
В Firefox все работает как положено.
Использование другой функции перехода (например, linear
) влияет только на переход контейнера; несоответствие остается.
Является ли это ошибкой в Chrome, и если да, то есть ли обходной путь?
.accordion {
display: grid;
grid-template-rows: minmax(0, 0fr) min-content;
transition: grid-template-rows 3000ms;
border: 1px solid;
padding: 1rem;
}
.accordion.open {
grid-template-rows: minmax(0, 1fr) min-content;
}
.content {
overflow: hidden;
}
<div class = "accordion">
<div class = "content">
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aperiam asperiores aut deserunt eaque incidunt labore laudantium minus nam omnis placeat quas quo, reprehenderit rerum soluta veritatis! Accusamus aliquam atque autem consequatur cumque, deserunt doloremque dolorum eaque eligendi esse est eum excepturi fugit hic impedit incidunt minus odit quae, ut voluptas.
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aperiam asperiores aut deserunt eaque incidunt labore laudantium minus nam omnis placeat quas quo, reprehenderit rerum soluta veritatis! Accusamus aliquam atque autem consequatur cumque, deserunt doloremque dolorum eaque eligendi esse est eum excepturi fugit hic impedit incidunt minus odit quae, ut voluptas.
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aperiam asperiores aut deserunt eaque incidunt labore laudantium minus nam omnis placeat quas quo, reprehenderit rerum soluta veritatis! Accusamus aliquam atque autem consequatur cumque, deserunt doloremque dolorum eaque eligendi esse est eum excepturi fugit hic impedit incidunt minus odit quae, ut voluptas.
</div>
<button onclick = "this.parentElement.classList.toggle('open');">toggle</button>
</div>
Я бы хотел избежать анимации grid-template-rows
при использовании значений меньше 1fr
и когда высота сетки зависит от высоты контента.
Обертывание новым элементом div и извлечение кнопки из сетки — одно из решений, которое должно сработать.
.accordion {
display: grid;
grid-template-rows: 0fr;
transition: grid-template-rows 3000ms;
overflow: hidden;
align-items: flex-start;
/* stretch is causing strange behavior, flex-start forces the element to have predictable height*/
}
.content {
overflow: hidden;
}
.accordion.open {
grid-template-rows: 1fr;
}
.wrapper {
border: 1px solid;
padding: 1rem;
}
button {
width: 100%
}
<div class = "wrapper">
<div class = "accordion">
<div class = "content">
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aperiam asperiores aut deserunt eaque incidunt labore laudantium minus nam omnis placeat quas quo, reprehenderit rerum soluta veritatis! Accusamus aliquam atque autem consequatur cumque, deserunt
doloremque dolorum eaque eligendi esse est eum excepturi fugit hic impedit incidunt minus odit quae, ut voluptas. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aperiam asperiores aut deserunt eaque incidunt labore laudantium minus nam
omnis placeat quas quo, reprehenderit rerum soluta veritatis! Accusamus aliquam atque autem consequatur cumque, deserunt doloremque dolorum eaque eligendi esse est eum excepturi fugit hic impedit incidunt minus odit quae, ut voluptas. Lorem ipsum
dolor sit amet, consectetur adipisicing elit. Aperiam asperiores aut deserunt eaque incidunt labore laudantium minus nam omnis placeat quas quo, reprehenderit rerum soluta veritatis! Accusamus aliquam atque autem consequatur cumque, deserunt doloremque
dolorum eaque eligendi esse est eum excepturi fugit hic impedit incidunt minus odit quae, ut voluptas.
</div>
</div>
<button onclick = "this.parentElement.children[0].classList.toggle('open');">toggle</button>
</div>
Другие решения также могут соответствовать вашим потребностям. Кажется, вы реализуете обходной путь для анимации height: auto
. Есть и другие обходные пути с другими компромиссами.
IE height: calc-size(auto);
перешел на chrome-canary, что должно облегчить переход с height: 0
на height: auto
. Но внедрение для всех браузеров, безусловно, займет некоторое время.
Единицы Fr: займите часть доступного пространства.
При переходе с 0fr
на 1fr
. мы будем на 0.5fr
на середине анимации.
В этот момент наши строки сетки будут 0.5fr
+ min-content
для двух строк.
Общая высота сетки: высота контента + высота кнопки = 100%
Теперь мы подошли к самой сути (и несоответствию в реализации браузера).
Что такое 0,5фр или 50% доступного места? Общее пространство зависит от дочернего размера, а дочерний размер зависит от общего пространства.
100px
), и он равен общему размеру = 100px
100px
), подсчитывает общую сумму (100px
), а затем снова масштабирует дочерние элементы до «доступного пространства» (0.5fr
, 50px
)Кто прав?
Не знаю, но мне больше подходит Firefox.
(Спорно) Хром! В спецификации это упоминается в определении «гибкого фактора»:
Примечание. Если сумма коэффициентов гибкости меньше 1, они займут только соответствующую часть оставшегося пространства, а не расширятся, чтобы заполнить все пространство.
В качестве примечания; fr
единицы и minmax
в основном полезны, когда есть какой-либо другой элемент, занимающий место. Если все единицы определены как fr
, вы также можете использовать проценты.
например
1fr 1fr
= 50% 50%
.0.5fr 1.5fr
= 25% 75%
minmax(200px, 1fr), 1fr
= когда > 400 пикселей 50% 50%
, когда < 400 200px remainder
, этого невозможно добиться с помощью одних процентов.minmax(0, 1fr), 1fr
= неважно какой размер 50% 50%
Сравнение Chrome и Firefox:
.accordion {
width: 150px;
display: grid;
grid-template-rows: .5fr min-content;
border: 1px solid;
padding: 1rem;
overflow: hidden;
}
.content {
overflow: hidden;
}
.align-start {
align-items: flex-start;
}
.example {
margin: 50px;
padding: 10px;
background: linen;
<div class = "example">Example: .5fr
<div class = "accordion">
<div class = "content">
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aperiam asperiores aut deserunt eaque incidunt labore laudantium minus nam omnis placeat quas quo, reprehenderit rerum soluta veritatis! Accusamus aliquam atque autem consequatur cumque, deserunt
doloremque dolorum eaque eligendi esse est eum excepturi fugit hic impedit incidunt minus odit quae, ut voluptas.
</div>
<button onclick = "this.parentElement.classList.toggle('open');">toggle</button>
</div>
</div>
<div class = "example">
Example: .5fr with 'align-items: flex-start'
<div class = "accordion align-start">
<div class = "content">
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aperiam asperiores aut deserunt eaque incidunt labore laudantium minus nam omnis placeat quas quo, reprehenderit rerum soluta veritatis! Accusamus aliquam atque autem consequatur cumque, deserunt
doloremque dolorum eaque eligendi esse est eum excepturi fugit hic impedit incidunt minus odit quae, ut voluptas.
</div>
<button onclick = "this.parentElement.classList.toggle('open');">toggle</button>
</div>
</div>
<div class = "example">
Example: .5fr with 'align-items: flex-start' moved button out of grid
<div class = "accordion align-start">
<div class = "content">
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aperiam asperiores aut deserunt eaque incidunt labore laudantium minus nam omnis placeat quas quo, reprehenderit rerum soluta veritatis! Accusamus aliquam atque autem consequatur cumque, deserunt
doloremque dolorum eaque eligendi esse est eum excepturi fugit hic impedit incidunt minus odit quae, ut voluptas.
</div>
</div>
<button onclick = "this.parentElement.classList.toggle('open');">toggle</button>
</div>
Ларс, блестящее объяснение! Спасибо
Спасибо за ответ! Я думаю, что объяснение имеет смысл, и решение сработало. Однако мне пришлось использовать minmax(0, 0fr)
и minmax(0, 1fr)
вместо 0fr
и 1fr
, чтобы заставить его работать.
Оказывается, Chrome (более) корректен в соответствии с актуальной спецификацией W3 (редакторский проект, 13 августа 2024 г.). Я обновил ответ. В качестве примечания: minmax
будет иметь какой-либо эффект только в том случае, если есть строка, отличная от fr
. minmax(0,0fr)
— это просто 0
, а minmax(0,1fr)
будет равняться 1fr, если других строк нет. Я бы не особо беспокоился по этому поводу, если бы у вас это работало, это просто меня немного удивило.
Связано, если не дублировать - stackoverflow.com/questions/43911880/…