Transition question

Discuss how to use and promote Web standards with the Mozilla Gecko engine.
Locked
Bethrezen
Posts: 445
Joined: September 13th, 2003, 11:56 am

Transition question

Post by Bethrezen »

Hi all

I have a question regarding transitions, now as I'm sure most are aware you can't transition from display:block to display:none because they are essentially binary options and are either on or off, and therefore there are no intermediate steps that can be animated.

That being the case why then can I transition visibility which like display:block, display:none is a binary options with no intermediate steps that can be animated.

Now I know that strictly speaking when you transition visibility you are not really transitioning visibility, what you are actually doing is combining visibility with something like opacity and then using the timing function to set a time delay on visibility, so that the visibility is flipped when the opacity fade is finished as opposed to right away which is the normal behaviour.

So something like this

transition-property: opacity;
transition-duration: 1s
transition-timing-function: linear;

transition-property: visibility;
transition-delay: 1s;

Now what I'm curious about is why when I specify a transition on display:block, display:none does it not work the same way as it does for visibility, in that I do an opacity fade over say 1 second and then flip the display from block to none after the 1 second fade has finished.

More over why do I even need to specify an opacity fade in the first place, because when transitioning both display:block, display:none and visibility:visible, visibility:hidden the default behaviour for both of these should be the same as if I set an opacity fade between the visible and invisible states.

Therefore in theory I shouldn’t actually need to set an opacity fade, because the default behaviour for transitioning both display:block, display:none and visibility:visible, visibility:hidden should insert an opacity fade between the visible and invisible states automatically, and then the states should simply be flipped at the start/end of the fade.

So when fading in

The visibility / display is flipped to either visible / block immediately, but opacity remains at 0 and then 10 milliseconds later the opacity fade starts to fade the element in over say 1 second or what ever duration is specified.

On fade out

The opacity fade runs fading the element out over say 1 second or what ever duration is specified, and then visibility / display is flipped to either hidden / none after the specified duration.

You can see the effect in this simple example just copy the code in to a new text document save it as a html file and then run it.

Code: Select all

<!DOCTYPE html>
<html lang="en-GB">
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">

<style>

#tooltip_container
{
width: 150px;
background-color:pink;
}

#tooltip_container p
{
margin:0;
padding:3px 5px;
}

.tooltip
{
width: 100px;

color:black;
background-color: rgb(255, 255, 225);
border:1px solid black;
}

</style>

</head>

<body>

<div id="tooltip_container">
    <p id="hover">Hover Me</p>

    <div class="tooltip">
        <p>hi im a tool tip</p>
    </div>
</div>

<script>

var element = document.getElementById("tooltip_container").querySelector("div")

element.style.cssText = "display:none; opacity: 0;";

function hoveron(event) 
{
  element.style.cssText = "display:block;opacity: 0;";
  setTimeout (fadein, 10);
}

function fadein()
{
  element.style.cssText = "opacity: 1; transition: opacity 0.7s ease-in-out;";
}

function hoveroff(event) 
{
  element.style.cssText = "opacity: 0; transition: opacity 0.7s ease-in-out;"; 
  setTimeout (fadeout, 700);
}

function fadeout()
{
  element.style.cssText = "display:none;";
}

document.getElementById("hover").addEventListener("mouseover", hoveron);
document.getElementById("hover").addEventListener("mouseout", hoveroff);

</script>

</body>
</html>
You will see how it's possible to insert an opacity fade between display:block / display:none and then flip the display from block to none removing the hidden element from the document flow after the fade is finished, thus preventing it from taking up space on the page while its hidden which is in most cases is the desired behaviour when someone is trying to do something like this.

When reversing the process you can see how again its possible to insert an opacity fade between display:block / display:none and then flip the display from none to block while still maintaining a smooth fade in animation, which is currently not possible with css alone since the transition time delay does not work with display:block, display:none the same way it does for visibility:visible, visibility:hidden and of course this same process will also work for other things like overflow which also do not currently work with transitions.

Now of course there are other ways to remove an element from the document flow such as using absolutely relative positioning aka the process where by you position something absolutely, within a relatively positioned parent, but I deliberately decided not to do that because it makes it difficult to see the effect.

Now while I'm sure this is a little crude and there are probably better ways to do this, given my limited knowledge of JavaScript ultimately this is how transitioning both display:block / display:none and visibility:visible, visibility:hidden should work by default and of course if something like this was the default implementation then the code would be optimised and would be much faster, simpler and much less bulky as all you would need to do to is specify

transition-property: display;
transition-duration: 1s
transition-timing-function: linear;
transition-delay: 1s;

and doing that would caused exactly the effect you see in my simple little example, a smooth fade in and out and and then at the start / end of the fade the viability/display would be flipped to either viable/hidden or block/none.
User avatar
jscher2000
Posts: 11742
Joined: December 19th, 2004, 12:26 am
Location: Silicon Valley, CA USA
Contact:

Re: Transition question

Post by jscher2000 »

For this example, I like a "roll-down" effect. Try removing your script and adding this CSS to your style block:

Code: Select all

.tooltip {
  overflow: hidden; /* Don't show contents while expanding */
  max-height: 0;    /* Crush height by default */
  opacity: 0;       /* Transparent content by default */
  transition: all 0.7s ease-in-out;
}
#tooltip_container:hover .tooltip {
  max-height: 45px; /* Enough for two lines */
  opacity: 1;       /* Full opacity */
  transition: all 0.7s ease-in-out;
}
Bethrezen
Posts: 445
Joined: September 13th, 2003, 11:56 am

Re: Transition question

Post by Bethrezen »

jscher2000 wrote:For this example, I like a "roll-down" effect. Try removing your script and adding this CSS to your style block:

Code: Select all

.tooltip {
  overflow: hidden; /* Don't show contents while expanding */
  max-height: 0;    /* Crush height by default */
  opacity: 0;       /* Transparent content by default */
  transition: all 0.7s ease-in-out;
}
#tooltip_container:hover .tooltip {
  max-height: 45px; /* Enough for two lines */
  opacity: 1;       /* Full opacity */
  transition: all 0.7s ease-in-out;
}
the problem with doing height / max-height: as opposed to using display: block / display:none; is that you must know the height of the box while in a simple demo using height / max-height may well do the trick if the height of the box is unknown then using height / max-height wont work. and of course when using visibility the hidden element still takes up space which in most instances is undesirable.

I have tried this before when i was building a responsive site, the issue i ran into though is that max height had to be a dynamic value otherwise the contents of the box ended up clipped or the box ended up way to tall.

so for example i set max height to be sufficient to display the boxes content at full screen when i made the screen smaller the content got clipped because the box was narrower because the window was smaller, however if i then set max height to be big enough that all the content would show in the smaller window when the window went back to full size the box was way to tall

i never did find a way around this without using scripting because at the time it simply wasn't possible to set the size of a box based on its content with css only.

I do know about min-content, fit-content, max-content keywords for sizing elements which would likely help eliminate a problem like this the issue is lack of support.

https://caniuse.com/#search=min-content
https://caniuse.com/#search=fit-content
https://caniuse.com/#search=max-content

the benefit of using display:none / display:block and then using a bit of scripting to insert an opacity fade and a time delay is that this will work no matter how much or how little content the box has and while its hidden it takes up no space, ultimately it would be best if transitions worked with display:none / display:block but i don't see that happening any time soon.
Locked