Css transition hover display block
One of the most common CSS3 transition animations used by developers in the coming years will undoubtedly be making elements appear and disappear via the opacity property. What previously was only possible using JavaScript can now be easily described in significantly less bulky CSS code:
* Note: Because the spec is still in flux, most browsers require a vendor-specific prefix such as -webkit-transition for Chrome/Safari, -moz-transition for Firefox and -o-transition for Opera to make CSS3 transitions work. For the sake of simplicity in the article, I will be using just plain transition in the examples.
The most exciting thing about CSS3 transitions is that, by design, they naturally fall back to the normal non-animated transitions for browsers which don’t support them. That means you can start using them in production designs today! Using opacity to fade in elements is great for all kinds of novel website effects which reduce reliance on JavaScript for things developers everywhere are already doing.
Take for example, the drop-down menu. For years developers have been creating snappy menus using only CSS :hover effects. It would only make sense to start applying for opacity transitions to these menus to make them just a little bit more slick. However, here is where we run into our first problem with using opacity .
An element with opacity of zero is still «opaque» to clicks and mouse-overs. This causes serious problems, especially for drop down menus:
div < background-color:#f6f6f6; padding:2px 5px; position:relative: >div > ul < list-style-type:none; margin:0px; padding:2px 10px; position:absolute; left:0px; top:100%; background-color:#eeeeee; opacity:0; transition:opacity 0.5s linear; > div:hover > ul < opacity:1; >
It looks nice, but the problem becomes obvious when you try to click the link. The fully transparent menu still appears when we hover any area it covers, essentially preventing all interaction with any content beneath. Not to mention the behaviour itself is quite odd, especially when moving the mouse toward the link from below. What we want is to prevent the menu from receiving hover events when it is fully transparent.
Okay, the display property can do that with the block and none values. Let’s give it a try:
Hey now, we can click that link without the menu appearing! But. it no longer animates in Chrome and Firefox, and it only animates the fade-in part in Opera. When you mouse out of the menu, it just vanishes. Well, this sucks; I guess we can’t use display with transitions if we want them to work reliably cross-browser.
Fortunately, there is another property we can use: visibility . Elements that are visibility:hidden are transparent to clicks and hover events, which is exactly what we want. Let’s do this!
Excellent! Now all the browsers behave the same way. Unfortunately they all behave the same way as Opera: The fade-in works fine, but there is no fade-out, it just disappears. To debug this problem, we need to delve deeper into how CSS3 transition timing actually works.
We have an element, the menu, which switches from hidden to visible on mouseover, and smoothly transitions from fully transparent to fully opaque in half a second. That seems logical, but why is the fade-out not happening when we reverse that process? The key is knowing at what point the switch from visibility:visible to visibility:hidden occurs. In both cases, this switch happens immediately.
- User mouses over the element
- visibility is switched to visible
- opacity transition animation begins
- opacity transition animation ends
- User mouses out of the element
- visibility is switched to hidden
- opacity transition animation begins
- opacity transition animation ends
In the second case, when visibility switches back to hidden , it sabotages our whole animation by hiding it. The animation still occurs, it’s just hidden from view. We really want the fade-out action timeline to look like this:
- User mouses out of the element
- opacity transition animation begins
- opacity transition animation ends
- visibility is switched to hidden
But how in the world can we delay a style from taking effe. Oh my goodness, CSS3 transitions can do that too! And lucky us, visibility is one of the select group of CSS properties which are affected by CSS transitions. Let’s slap on a visibility transition!
Quick explanation: We added a visibility transition with a 0s animation time, a linear animation curve, and we also used the optional delay argument to set an animation delay of 0.5s . This means there will be a half second delay before the transition takes place, which exactly equals the time taken by our opacity fade out. And.
. okay the fade-out works, but adding that code ruined the fade-in which was working so nicely before. What gives? The delay we added to the visibility property is taking effect both during the fade-in and the fade-out. All we’ve done is made our fade-in action timeline look like this:
- User mouses over the element
- opacity transition animation begins
- opacity transition animation ends
- visibility is switched to visible
ARGH! Now what? We want the visibility to change with no delay on the fade-in, but with a half-second delay on the fade-out. Is that even possible? It seems like there is no way to specify different transition properties for the two halves of the animation effect. Is all lost? Must we resort to JavaScript?
There is one more thing we can try. Like in many CSS modules, such as border and background , the CSS3 transition property is actually a shortcut property which groups several properties you can specify individually. One of these properties is transition-delay . Using this property we want to temporarily set the delay of the visibility transition to 0s only during the fade-in portion of the animation. But how do we specify that?
Oh yeah! The fade-in only happens while the user has their mouse over the element. The fade-out doesn’t happen until the mouse cursor is removed. So we can apply this special transition-delay to the :hover state of the element.
You’ll notice that we only specified one delay time, while we are using two transitions: visibility and opacity . According to the CSS3 specification, when there aren’t enough delay values to go around, they are repeated to apply to all transitions. In this case, the opacity transition had a 0s delay by default so it actually doesn’t change.
How about that, it works! With a temporary change in the delay value of the transition, we successfully created a drop-down menu that both fades-in and fades-out very nicely, and also doesn’t interfere with content beneath it. But is looking nice the end of the story?
Heavens no! Drop-down menus using :hover have been blasted by usability experts for years; especially when you start adding in sub-menus. If you move your mouse just one pixel outside of an element in the :hover chain, the whole menu up to that point will collapse instantly, likely hiding the menu option you were trying to get to.
All that changes with CSS opacity transitions. When your mouse cursor travels outside a :hover chain element, its children don’t disappear immediately, but rather start the opacity animation to transparency. Before the animation has completed, the element is still available to receive :hover events. Therefore if you move you mouse back onto the element quickly, the chain won’t collapse and your target will still be available to select. This solves the dreaded «diagonal problem» that plagues so many CSS drop-down menu implementations.
Who knew CSS3 transitions could be for more than just fancy effects? They’re a boon for menu usability as well! In the future, I definitely expect we will see many more uses for transitions beyond simply prettying up websites. Perhaps you are tinkering with some already.
Comments closed
# Comment by Jake
# Comment by Simone
Well, a very interesting article! Thanks! But, I’ve a problem with webkit browser (both Chrome and Safari). Some animation run the «fadeout» effect (the same that happens on mouse out) immediately on the page load. Only Firefox works as aspected. I don’t know why only webkit fails on this. Thanks.
Aug 19, 2011 — 15:21
# Comment by Cory
# Comment by Justin
# Comment by Brian
# Comment by Danny
Recent posts
© 2023 Brian Huisman AKA GreyWyvern
Contact • Site map • Search
Анимация перехода display из none в block
Есть некий объект со свойством display: none . Нужно анимировать его появление, при изменении параметра display , например, на block .
Пример
Невидимый прямоугольник делаем видимым при нажатии на кнопку:
document.addEventListener("DOMContentLoaded", () => const button = document.getElementById("button"); const rect = document.getElementById("rect"); button.addEventListener("click", () => rect.classList.toggle("is-visible"); >); >);
#rect width: 100px; height: 100px; background-color: red; display: none; > #rect.is-visible display: block; >
Проблема
Стандартный способ анимации через transition не работает. Но это и не удивительно:
#rect width: 100px; height: 100px; background-color: red; transition: all 0.5s ease-in-out; display: none; >
Но даже через введение параметра opacity ничего не работает:
#rect width: 100px; height: 100px; background-color: red; transition: all 0.5s ease-in-out; display: none; opacity: 0; > #rect.is-visible display: block; opacity: 1; >
Решение
Будем использовать @keyframes :
#rect width: 100px; height: 100px; background-color: red; display: none; opacity: 0; > #rect.is-visible display: block; opacity: 1; animation: fadeInFromNone 3s ease-in-out; > @keyframes fadeInFromNone 0% display: none; opacity: 0; > 1% display: block; opacity: 0; > 100% display: block; opacity: 1; > >
Решение с обратной анимацией
Сейчас анимирован только переход из невидимого состояния в видимое. Сделаем так, чтобы работало и в обратную сторону. Как оказалось, это гораздо сложнее.
#rect width: 100px; height: 100px; background-color: red; display: none; opacity: 0; > #rect.is-visible display: block; animation: fadeInFromNone 0.5s ease-in-out; animation-fill-mode: forwards; > #rect.is-hidden animation: fadeOutFromBlock 0.5s ease-in-out; > @keyframes fadeInFromNone 0% opacity: 0; > 100% opacity: 1; > > @keyframes fadeOutFromBlock 0% opacity: 1; > 100% opacity: 0; > >
document.addEventListener("DOMContentLoaded", () => const button = document.getElementById("button"); const rect = document.getElementById("rect"); button.addEventListener("click", () => toggleTwoClasses(rect, "is-visible", "is-hidden", 500); >); >); function toggleTwoClasses(element, first, second, timeOfAnimation) if (!element.classList.contains(first)) element.classList.add(first); element.classList.remove(second); > else element.classList.add(second); window.setTimeout(function() element.classList.remove(first); >, timeOfAnimation); > >
Если добавим в HTML первый класс is visible , то вначале элемент будет видимым, а потом переключаться с видимого на невидимый:
id="button">Show id="rect" class="is-visible">