Вы всё ещё устанавливаете display:none по таймауту? Тогда мы идём к вам
- суббота, 28 декабря 2024 г. в 00:00:10
Дисклеймер: хотел создать скромный пост, а не статью, но не справился с управлением новым редактором. А старый редактор не поддерживает посты.
Допустим, у нас есть блок (скажем, бутстраповская ячейка <div class="col-12">
) и мы хотим её схлопывать (скажем, по клику на кнопке).
Пишем пару простых классов:
:root
{
…
--fast: 0.4s;
--all-fast-ease: all var(--fast) ease;
…
}
.all-fast-ease
{
transition: var(--all-fast-ease);
}
.collapsed
{
overflow: hidden;
height: 0;
}
Помечаем наш блок классом all-fast-ease
, чтобы он сворачивался плавно:
<div class="col-12 all-fast-ease">
Затем добавляем класс collapsed
в коде обработчика (везде используется jQuery):
$('.contact-form .hugh-mann-detector').on("click", function ()
{
…
if (100 == progressValue) // Пять кликов, progressValue += 20
{
$(this)
.data('is-hugh-mann', true)
.parent().addClass('collapsed');
}
}
И… ничего не происходит. Всё правильно: мы пытаемся анимировать высоту (height
) к значению 0
из значения auto
. А для этого нам требуется явно сообщить о своих намерениях:
.collapsed
{
overflow: hidden;
height: 0;
interpolate-size: allow-keywords; /* Chrome 129+ */
}
Значение allow-keywords
поддерживается в Chrome и Edge уже пару версий, и, надо полагать, Firefox с Safari тоже скоро подтянутся.
Однако, если блок содержит элементы управления (а в нашем случае он содержит кнопку), нас ждёт сюрприз. Оформить «детектор Чела Века» (.hugh-mann-detector
) именно как кнопку нужно из соображений accessibility. Во-первых, до неё должно быть можно добраться при помощи клавиатуры (Tab
), если человеку трудно двигаться. Во-вторых, скринридер должен сообщить о наличии кнопки, которую можно нажать.
Но именно по этой причине кнопка, даже скрытая в блоке нулевой высоты, крадёт клавиатурный фокус. А это говорит о том, что свёрнутый блок надо скрывать полностью, например, задав ему display: none
.
Первым делом хочется написать setTimeout
и отложенно добавить к нашему блоку класс d-none
.
Проблема в том, что для этого нам нужно как-то найти время анимации. Конечно, можно просто явно указать значение (400 миллисекунд), благо мы сами описали анимацию.
Но это нарушит принцип DRY («не сотвори себе копипасту»). Проблема осложняется тем, что у нас период времени вынесен в отдельную переменную (--fast
), и если мы будем определять её значение, а в разметке all-fast-ease
будет заменён на какой-нибудь all-slow-ease
, мы об этом не узнаем. Другое осложнение в том, что значение придётся парсить, чтобы получить миллисекунды.
А это значит, что лучше всего анимировать display
там же, где происходит и схлопывание. Да, с августа теперь так можно! Создадим специализацию для быстро-анимированного-свёрнутого блока:
.collapsed.all-fast-ease
{
transition: var(--all-fast-ease), display var(--fast) ease allow-discrete;
}
Теперь display
будет анимироваться наравне с высотой, поскольку мы задали поведение allow-discrete
. Можно было бы просто объединить его с прочими анимациями (вписав allow-discrete
в конец переменной --all-fast-ease
), но это нарушило бы ещё один принцип: указывать только то, что нужно (так получается более универсальная стилизация с меньшим количеством сюрпризов).
И теперь вместо setTimeout
достаточно написать в обработчике события:
.parent().addClass('collapsed d-none');
Результат вы видели на КДПВ.