CSS Transition on an Initially Hidden Elemement

CSS transition on an initially hidden elemement

Regarding the question on if this is in the spec, there is an interesting thread on the www-style@w3.org list here. I haven't read it all but it seems as they don't start animations from none and that the transition spec needs to clarify that as well.

Update: I have asked the mail list and I got this link to the minutes of a work group meeting where it was decided that there should be no transition if the start state is display: none.

To make sure that the transition is performed you must make sure that the value of the animated property is calculated before it is set to its new target. Values are normally not calculated when display is set to none. Here is a working example:

<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">

<title>Fade</title>

<style>
.hidden { display: none }
.start { opacity: 0 }
.transition { opacity: 1; -webkit-transition: opacity 1s }
</style>
</head>
<body>
<div id='div' class="hidden">Test</div>

<script>
var elem = document.getElementById('div');

function ontransitionend(event) {
elem.className = null;
elem.removeEventListener('transitionend', ontransitionend);
}
elem.addEventListener('transitionend', ontransitionend);

elem.className = 'start';
window.getComputedStyle(elem).opacity;
elem.className = 'transition';
</script>
</body>
</html>

Note that you have to access the opacity property. It is not enough to call getComputedStyle()!

make a transition with a hidden element

You can use this CSS and keep the JS the way it is:

#Hvideo[hidden] {
display: block;
visibility: visible;
opacity: 0;
}
#Hvideo:not([hidden]) {
opacity: 1;
}
#Hvideo {
transition: opacity 0.4s ease-in-out;
}

css transition doesn't work if element start hidden

To understand plainly the situation, you need to understand the relation between the CSSOM and the DOM.

In a previous Q/A, I developed a bit on how the redraw process works.

Basically, there are three steps, DOM manipulation, reflow, and paint.

  • The first (DOM manipulation) is just modifying a js object, and is all synchronous.
  • The second (reflow, a.k.a layout) is the one we are interested in, and a bit more complex, since only some DOM methods and the paint operation need it. It consists in updating all the CSS rules and recalculating all the computed styles of every elements on the page.

    Being a quite complex operation, browsers will try to do it as rarely as possible.
  • The third (paint) is only done 60 times per seconds at max (only when needed).

CSS transitions work by transitioning from a state to an other one. And to do so, they look at the last computed value of your element to create the initial state.

Since browsers do recalculate the computed styles only when required, at the time your transition begins, none of the DOM manipulations you applied are effective yet.

So in your first scenario, when the transition's initial state is calculated we have

.b { computedStyle: {display: none} }

... and that's it.

Because, yes, that's how powerful display: none is for the CSSOM; if an element has display: none, then it doesn't need to be painted, it doesn't exist.

So I'm not even sure the transition algorithm will kick in, but even if it did, the initial state would have been invalid for any transitionable value, since all computed values are just null.

Your .a element being visible since the beginning doesn't have this issue and can be transitioned.

And if you are able to make it work with a delay (induced by $.animate), it's because between the DOM manip' that did change the display property and the execution of this delayed DOM manip' that does trigger the transition, the browser did trigger a reflow (e.g because the screen v-sync kicked in between and that the paint operation fired).


Now, it is not part of the question, but since we do understand better what happens, we can also control it better.

Indeed, some DOM methods do need to have up-to-date computed values. For instance Element.getBoundingClientRect, or element.offsetHeight or getComputedStyle(element).height etc. All these need the entire page to have updated computed values so that the boxing are made correctly (for instance an element could have a margin pushing it more or less, etc.).

This means that we don't have to be in the unknown of when the browser will trigger this reflow, we can force it to do it when we want.

But remember, all the elements on the page needs to be updated, this is not a small operation, and if browsers are lenient to do it, there is a good reason.

So better use it sporadically, at most once per frame.

Luckily, the Web APIs have given us the ability to hook some js code just before this paint operation occurs: requestAnimationFrame.

So the best is to force our reflow only once in this pre-paint callback, and to call everything that needs the updated values from this callback.

$('button').on('click',function(){  $('.b').show(); // apply display:block synchronously    requestAnimationFrame(() => { // wait just before the next paint    document.body.offsetHeight; // force a reflow    // trigger the transitions    $('.b').css('right','80%');    $('.a').css('right','80%');  });})
body {  width:800px;  height:800px;}
div { width:50px; height:50px; background-color:#333; position:absolute; display:none; right:5%; top:0; transition:right .5s cubic-bezier(0.645, 0.045, 0.355, 1); color: white;}
.a { display:block; top:60px;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script><div class='a'>A</div><div class='b'>B</div><button>Launch</button>

Transitions on the CSS display property

You can concatenate two transitions or more, and visibility is what comes handy this time.

div {  border: 1px solid #eee;}div > ul {  visibility: hidden;  opacity: 0;  transition: visibility 0s, opacity 0.5s linear;}div:hover > ul {  visibility: visible;  opacity: 1;}
<div>  <ul>    <li>Item 1</li>    <li>Item 2</li>    <li>Item 3</li>  </ul></div>

Show/Hide with CSS transition

Since you are using height: 0 on the hidden class, this will execute firstly and you won't see any transition during the hiding process. You can achieve this with multiple tricks and ways, like the CSS animation and do stuff in a certain order or add a delay to your height adjustment and make it execute after the transition happens.

You can use the setTimeout method to make this happen.

const div = document.querySelector("div")
const button = document.querySelector("button")

button.addEventListener("click", () => {
if (div.classList.contains("hidden")) {
div.classList.remove("hidden")
div.classList.add("shown")
div.style.height = 'auto'
} else {
div.classList.remove("shown")
div.classList.add("hidden")

setTimeout(() => {
div.style.height = 0
}, 1000)
}
})
.div {
transition: all 2s;
}

.hidden {
opacity: 0;
overflow: hidden;
visibility: hidden;
}

.shown {
opacity: 1;
visibility: visible;
}
<div class="div">div</div>
<button>change visibility</button>

How to hide an element completely after a transition

is this what you need?

fieldset{  display:inline-block;  position:relative;  width:200px;  height:auto;  border:none;  background:#26a69a;}label{  display:inline-block;  vertical-align:middle;  color:white;  position:relative;  z-index:1;  transition:opacity 385ms cubic-bezier(0.23, 1, 0.32, 1);}input{  position:absolute;  z-index:2;  width:5px;  right:5px;  top:8px;  transition:all 385ms cubic-bezier(0.23, 1, 0.32, 1);}
fieldset:hover label{ opacity:0;}
fieldset:hover input{ width:195px; right:10px;}
<fieldset>  <label>Hover on me</label>  <input type="text"/></fieldset>

Transition with hidden elements

First method

I have managed to achieve what you wanted using opacity, according to (I think) MDN (Mozilla Developer Network) you can't transition display. I also rearranged your css slightly:

http://codepen.io/hwg/pen/xFDIl - Working Code.

CSS:

.design span {
position: absolute;
opacity:0;
transition: opacity 1s;

display: block;
top: 47px;
left: 45px;
font-family: Arial;
font-size: 13px;
color: #000;

width:0;
height:0;
margin-left:-1000px;

}

.design:hover span {

opacity:1;
/*transition: all 1s;*/

width:30px;
height:auto;
margin-left:0;
}

Note that codepen.io uses prefix-free, see http://caniuse.com/css-transitions for browser support, prefixes may have to be added.

Edit- to avoid the OP's problem below, add: width:0; height:0; to the un-hovered element, and a pixel width/height such as width:30px; height:auto; on hover. (See example) There is if you hover over precisely that area you see the hover effect, but in practice this would be very rare in my view, also this can be solved by the use of margin left if needed.

Note: this stops the transition on mouse out.

New Method

To avoid the problem mentioned above, the property pointer-events:none can be added to the span. This removes the need to have any negative margins, or custom widths or heights.

See http://codepen.io/hwg/pen/BojpK

However, this is not supported in IE-anything, see css 'pointer-events' property alternative for IE for maybe more help.

Otherwise I don't think the OP's aim can be completely met.



Related Topics



Leave a reply



Submit