Переходы на свойстве display в CSS
Я разрабатываю меню "мега-дропдаун" с помощью CSS — фактически, это обычное выпадающее меню только на CSS, но содержащее различные типы контента.
На данный момент, выяснил, что переходы CSS3 не применяются к свойству 'display', т.е. невозможно сделать плавный переход от display: none
к display: block
(или любой другой комбинации).
Существует ли способ сделать так, чтобы подменю из приведенного выше примера "появлялось" при наведении на один из элементов верхнего уровня?
Я знаю, что можно использовать переходы со свойством visibility:
, но не могу придумать, как это эффективно реализовать.
Я также пробовал использовать высоту, но это просто не сработало.
Я понимаю, что это легко достигнуть с помощью JavaScript, но хотел бы бросить себе вызов и обойтись только CSS, и, кажется, у меня не хватает идей.
5 ответ(ов)
Чтобы добиться желаемого эффекта, вам нужно скрывать элемент другим способом. Я достиг этого эффекта, установив оба <div>
абсолютно и задав скрытому элементу opacity: 0
.
Если вы попытаетесь переключить свойство display
с none
на block
, переходы для других элементов не сработают.
Чтобы обойти это ограничение, всегда позволяйте элементу оставаться со свойством display: block
, но скрывайте его, используя один из следующих способов:
- Установите
height
в0
. - Установите
opacity
в0
. - Позиционируйте элемент за пределами видимости другого элемента с
overflow: hidden
.
Скорее всего, есть и другие решения, но вы не сможете выполнить анимацию, если переключите элемент на display: none
. Например, вы можете попробовать что-то вроде этого:
div {
display: none;
transition: opacity 1s ease-out;
opacity: 0;
}
div.active {
opacity: 1;
display: block;
}
Но это не сработает. Из моего опыта, это не приведет ни к чему.
Поэтому вам всегда нужно оставлять элемент с display: block
— но вы можете обойти это, сделав что-то вроде этого:
div {
transition: opacity 1s ease-out;
opacity: 0;
height: 0;
overflow: hidden;
}
div.active {
opacity: 1;
height: auto;
}
Я подозреваю, что причина, по которой анимации отключены при изменении свойства display
, заключается в том, что это свойство выполняет совершенно другую функцию. Оно не изменяет что-то, что можно было бы плавно анимировать.
display: none;
и visibility: hidden;
— это две совершенно разных вещи.
Обе инструкции делают элемент невидимым, но при использовании visibility: hidden;
элемент всё равно отображается в раскладке, просто не видимо.
Скрытый элемент продолжает занимать пространство и отображается как инлайн-элемент или блочный, в зависимости от значения свойства display
. Другие элементы при этом не перемещаются автоматически, чтобы занять это пространство. Скрытый элемент просто не выводит свои пиксели на экран.
С другой стороны, display: none
полностью предотвращает отображение элемента.
Он не занимает никакого места в раскладке.
Элементы, которые занимали бы часть или всё пространство, занимаемое этим элементом, теперь подстраиваются, как будто этот элемент просто не существует.
Свойство display
— это не просто ещё один визуальный атрибут.
Оно задаёт весь режим отображения элемента, включая такие значения, как block
, inline
, inline-block
, table
, table-row
, table-cell
, list-item
и так далее!
Каждое из этих значений имеет свои особенности раскладки, и не существует разумного способа анимировать или плавно переключать их (представьте себе плавный переход от block
к inline
и наоборот!).
Именно поэтому анимации отключены при изменении свойства display (даже если изменение происходит на none
или с него — none
это не просто невидимость, это свой собственный режим отображения элемента, который означает отсутствие отображения вообще!).
Я нашел более эффективный способ решения этой задачи. Вы можете использовать CSS-анимации, чтобы создать отличный эффект отображения элементов.
.item {
display: none;
}
.item:hover {
display: block;
animation: fade_in_show 0.5s;
}
@keyframes fade_in_show {
0% {
opacity: 0;
transform: scale(0);
}
100% {
opacity: 1;
transform: scale(1);
}
}
Таким образом, при наведении на элемент он плавно появляется с анимацией. Это добавляет красивый визуальный эффект без необходимости использовать JavaScript.
Проблема, с которой вы столкнулись, связана с тем, как работает свойство display
в CSS-анимациях. В вашем коде анимация hide
корректно изменяет opacity
, однако свойство display
не может быть анимировано так, как вы того ожидаете.
Вот что происходит в вашем примере: в течение 99% времени анимации display
устанавливается на block
, а на 100% оно устанавливается на none
. Но браузеры не применяют display: none
в анимации, потому что это свойство не может быть интерполировано и не имеет промежуточных состояний.
Кроме того, важно помнить, что когда вы задаете animation-fill-mode: forwards
, это означает, что после окончания анимации элемент сохранит стили последнего ключевого кадра. В данном случае, final state (display: none
) не применяется, поскольку элемент в это время уже был отключен от потока документа (потому что display: none
не сохраняет состояние после анимации).
Чтобы достичь нужного вам эффекта, попробуйте изменить подход. Например, вы можете комбинировать анимации opacity
и динамически устанавливать display
через JavaScript после завершения анимации. Вот пример:
@keyframes fade-out {
0% {
opacity: 1;
}
99% {
opacity: 0;
}
100% {
opacity: 0; /* Применяем opacity: 0, а display меняем через JS */
}
}
.hide {
animation: fade-out 1s linear forwards;
}
Затем в вашем JavaScript добавьте обработчик события, который устанавливает display: none
после окончания анимации:
const element = document.querySelector('.hide');
element.addEventListener('animationend', () => {
element.style.display = 'none'; // устанавливаем display: none
});
Таким образом, вы можете добиться желаемого эффекта скрытия элемента с плавным исчезанием.
Вот перевод вашего ответа в стиле StackOverflow:
Мой удобный трюк с JavaScript заключается в разделении всего сценария на две разные функции!
Для подготовки я объявляю одну глобальную переменную и определяю один обработчик события:
var tTimeout;
element.addEventListener("transitionend", afterTransition, true); // для Firefox
element.addEventListener("webkitTransitionEnd", afterTransition, true); // для Chrome
Затем, когда необходимо скрыть элемент, я использую что-то вроде этого:
function hide() {
element.style.opacity = 0;
}
function afterTransition() {
element.style.display = 'none';
}
Для того чтобы элемент снова появился, я делаю что-то похожее на это:
function show() {
element.style.display = 'block';
tTimeout = setTimeout(timeoutShow, 100);
}
function timeoutShow() {
element.style.opacity = 1;
}
На данный момент всё работает прекрасно!
Как перейти от height: 0; к height: auto; с помощью CSS?
Получить координаты (X,Y) HTML-элемента
Стилизация кнопки input type="file"
Изменение цвета элемента hr
Использование подстановочного знака * в CSS для классов