Вы всё ещё устанавливаете display:none по таймауту? Тогда мы идём к вам

от автора

Дисклеймер: хотел создать скромный пост, а не статью, но не справился с управлением новым редактором. А старый редактор не поддерживает посты.

Допустим, у нас есть блок (скажем, бутстраповская ячейка <div class="col-12">) и мы хотим её схлопывать (скажем, по клику на кнопке).

image

Пишем пару простых классов:

: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 миллисекунд), благо мы сами описали анимацию.

image

Но это нарушит принцип 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');

Результат вы видели на КДПВ.


ссылка на оригинал статьи https://habr.com/ru/articles/870172/