Css Transition Doesn't Work If Element Start Hidden

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>

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;
}

Are CSS3 transitions disabled when element is hidden?

According to MDN:

Display

In addition to the many different display box types, the value none lets you turn off the display of an element; when you use none, all descendant elements also have their display turned off. The document is rendered as though the element doesn't exist in the document tree.

So i think elements with display set to none will not be rendered at all across all browsers and therefore transition or any other visual effect won't be applied.

You can also test yourself by subscribing to transitionend event:

$(element).on("transitionend", function () {
console.log("transition ended");
});

Update:

It is also per w3c specification:

And some values (such as display:none, display: contents, and box-suppress: discard) cause the element and/or its descendants to not generate any boxes at all.

Where boxes are visual representations of element. And transition is definitely a part of visual representation as it also can affect layout e.g. when you change relative position of element with transition applied.


Here is one more example of how different are animations of elements with display : none and visibility : hidden in other words of rendered element and not-rendered one.

JSFiddle DEMO

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>

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()!

CSS transitions: Why won't my CSS transition work as I expect?

EDIT: after your comment reply, what you need to do is trigger the fadeout on a focus event.

.flash:focus {
//use the fadeout code here
}

The reason is the toggle, your browser saves the state of the page in the browser's cache but not the javascript that is dynamically changing the css on reload, on and off.

Instead of manipulating the css with javascript, google fade out with css.

.fade-out {
animation: fadeOut ease 10s;
-webkit-animation: fadeOut ease 10s;
-moz-animation: fadeOut ease 10s;
-o-animation: fadeOut ease 10s;
-ms-animation: fadeOut ease 10s;
}
@keyframes fadeOut {
0% {
opacity:1;
}
100% {
opacity:0;
}
}

it's a lot smoother this way, and you can test it more easily with the developer tools.

CSS Transition: opacity and visibility transition not working on Firefox (works on Chrome / Safari)

I think this is due to when the visibility is changed in the transition and seems to display inconsistently between browsers.

This demonstrates your code and for me in Firefox if you toggle the element quickly it does not transition smoothly. This is always how I've done similar transitions and only recently started noticing the problem.

var element = document.querySelector(".element")var toggle = document.querySelector(".element-toggle")
toggle.addEventListener("click", function(event) { element.classList.toggle("active");});
.element{  opacity: 0;  visibility: hidden;  transition: opacity 500ms ease, visibility 500ms ease;}
.element.active{ opacity: 1; visibility: visible;}
<div class="element">This is a div element</div>
<button type="button" class="element-toggle">Toggle</button>

Why does css transition occur upon revealing an element, but doesn't upon hiding?

Option 1:

Generally animating/transitioning from 0 to auto does not work. Changing the height from auto to some fixed value for the .toReveal CSS class will fix the issue.

.toReveal{
overflow: hidden;
height: 30px; /* set height such that it is big enough to accomodate its contents.*/
}
.toReveal * {
overflow: hidden;
height: auto;
}

Note: The transition part from height: 0 to height: auto is already working for you by using Option 2. But you might want to have a look at this article also.


Option 2: (used by OP based on feedback to comments)

Remove the overflow: hidden and it seems to fix the issue.

Also, as you have mentioned in the OP comment, adding display: block will make it slide from top because <input> is inline by default.

Modified CSS

.toReveal, .toReveal * {
display: block;
height: auto;
}

Working Fiddle

Alternatively, adding overflow: visible !important; to the .hidden, .hidden * CSS also seems to work.

CSS Transitions Not working after toggle between classes

TL;DR. See the code snippet below which is slightly tweaked from your original code.

A few notes:

  1. Consider using button instead of a tag. It's a button that does something when clicked as opposed to a hyperlink that directs to a link when clicked.
  2. Be careful with overly specific CSS rules. div.button-dropdown-options (i.e. element name + class name) is more specific than .button-dropdown-options (i.e. class name only), and can trump other less specific selectors. It is what makes you overly rely on !important in multiple places. Over specificity and !important in combination will make it very hard to debug styling.
  3. visibility as part of transition property as well. That is, something like transition: opacity 2s, visibility 2s;

function showDropDown() {
const variantButton = document.getElementById('showdropdown');
const variantMenu = document.getElementById('variantMenu');
const animate_arrow = document.querySelector('.btn-dropdown-caret');
const options = document.querySelector('.btn-dropdown-options');
const over = document.querySelector('.swiper-container');
const calculatedOptions = window.getComputedStyle(options);

variantButton.addEventListener('click', function(event) {
variantMenu.classList.toggle('dropdownShow');
animate_arrow.classList.toggle('animate-arrow');
});

}
showDropDown();
.btn-dropdown {
display: block;
position: relative;
white-space: nowrap;
margin-top: 5px;
margin-left: 2px;
}

@media screen and (min-width: 768px) {
.btn-dropdown {
z-index: 4;
}
}

.btn-dropdown .btn-dropdown-link {
-webkit-box-sizing: border-box;
box-sizing: border-box;
width: 100%;
}

.form-btn.outlined.btn-dropdown-link {
font-family: "Haas Grot Text R Web", "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 14px;
font-weight: 400;
line-height: 20px;
/* -webkit-transition: color 200ms ease;
transition: color 200ms ease; */
border: 1px solid #dddddd;
box-shadow: none;
-webkit-touch-callout: none;
/* iOS Safari */
-webkit-user-select: none;
/* Safari */
-khtml-user-select: none;
/* Konqueror HTML */
-moz-user-select: none;
/* Old versions of Firefox */
-ms-user-select: none;
/* Internet Explorer/Edge */
user-select: none;
/* Non-prefixed version, currently */
transition: all 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94) !important;
color: #6e6d7a;
}

.form-btn.outlined.btn-dropdown-link:hover,
.form-btn.outlined.btn-dropdown-link:focus {
border: 1px solid #e4b29b;
color: #1b1b1b;
box-shadow: 0 0 0 2px rgb(228 178 155 / 10%);
}

.form-btn.outlined.btn-dropdown-link:hover svg,
.form-btn.outlined.btn-dropdown-link:focus svg {
fill: #666666;
}

.animate-arrow {
transition: transform 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94);
transform: translateY(-50%) rotate(180deg) !important;
}

.form-btn.outlined,
a.form-btn.outlined {
background-color: transparent;
-webkit-box-shadow: 0px 0px 0px 1px #e7e7e9 inset;
box-shadow: 0px 0px 0px 1px #e7e7e9 inset;
color: #0d0c22;
}

.btn-dropdown .btn-dropdown-link {
-webkit-box-sizing: border-box;
box-sizing: border-box;
padding-right: 35px;
text-align: left;
}

.form-btn,
a.form-btn {
background: #f3f3f4;
color: #0d0c22;
}

.form-btn,
a.form-btn {
display: inline-block;
position: relative;
-webkit-box-sizing: border-box;
box-sizing: border-box;
height: 40px;
padding: 10px 16px;
-webkit-transition: color 200ms ease;
transition: color 200ms ease;
border: none;
border-radius: 10px;
outline: none;
background: #ea4c89;
text-align: center;
text-decoration: none;
cursor: pointer;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
font-family: "Haas Grot Text R Web", "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 14px;
font-weight: 500;
line-height: 20px;
}


/* Variant Selector --Span */

.btn-dropdown.btn-dropdown-neue svg.btn-dropdown-caret {
fill: #9e9ea7;
}

.btn-dropdown svg.btn-dropdown-caret {
position: absolute;
top: 50%;
right: 15px;
width: 10px;
height: 10px;
margin: 0;
-webkit-transform: translateY(-50%) rotate(0deg);
-ms-transform: translateY(-50%) rotate(0deg);
transform: translateY(-50%) rotate(0deg);
-webkit-transition: -webkit-transform 0.2s ease-in-out;
transition: -webkit-transform 0.2s ease-in-out;
transition: transform 0.2s ease-in-out;
transition: transform 0.2s ease-in-out, -webkit-transform 0.2s ease-in-out;
fill: currentColor;
}

.form-btn svg,
button.form-btn svg {
width: 13px;
height: 13px;
margin-top: -3px;
margin-right: 2px;
fill: currentColor;
vertical-align: middle;
}


/* DropDown */

.btn-dropdown .btn-dropdown-options {
right: 0;
}

.btn-dropdown div.btn-dropdown-options {
z-index: 2;
}

.btn-dropdown-options {
font-family: "Haas Grot Text R Web", "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 14px;
font-weight: 400;
line-height: 20px;
position: absolute;
z-index: 1;
top: calc(100% + 8px);
left: 0;
min-width: 180px;
margin-top: 4px;
overflow: auto;
border: 1px solid rgba(0, 0, 0, 0.05);
border-radius: 8px;
background: #fff;
visibility: hidden;
opacity: 0;
transition: opacity 2s, visibility 2s;
box-shadow: 0px 1px 2px rgba(128, 138, 157, 0.12), 0px 8px 32px rgba(128, 138, 157, 0.24);
}

.btn-dropdown-options ul {
padding: 8px 0;
list-style: none;
}

.btn-dropdown-options li.active a {
color: #ea4c89;
font-weight: 500;
text-decoration: none;
}

.btn-dropdown-options a {
display: block;
padding: 8px 15px;
color: #6e6d7a;
font-size: 13px;
text-decoration: none;
}



.btn-dropdown-options.dropdownShow {
/* animation: 0.3s ease 0s 1 normal forwards running dropdownAnimation !important; */
visibility: visible;
opacity: 1;
}

.visuallyhidden {
opacity: 0;
}
<span class="btn-dropdown btn-dropdown-neue">
<button id="showdropdown" class="form-btn outlined btn-dropdown-link" data-dropdown-state="closed">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" role="img" class="icon btn-dropdown-caret">
<path d="M21.5265 8.77171C22.1578 8.13764 22.1578 7.10962 21.5265 6.47555C20.8951 5.84148 19.8714 5.84148 19.24 6.47555L11.9999 13.7465L4.75996 6.47573C4.12858 5.84166 3.10492 5.84166 2.47354 6.47573C1.84215 7.10979 1.84215 8.13782 2.47354 8.77188L10.8332 17.1671C10.8408 17.1751 10.8486 17.183 10.8565 17.1909C11.0636 17.399 11.313 17.5388 11.577 17.6103C11.5834 17.6121 11.5899 17.6138 11.5964 17.6154C12.132 17.7536 12.7242 17.6122 13.1435 17.1911C13.1539 17.1807 13.1641 17.1702 13.1742 17.1596L21.5265 8.77171Z"></path>
</svg>
<span data-prompt="Variant" data-fade-default="true" class="default-option">
Sandle Brown
</span>
</button>

<div id="variantMenu" class="btn-dropdown-options sets-querystring">
<ul>
<li class="default-filter-option active"><a href="?timeframe=">Now</a></li>
<li class="default-filter-option active"><a href="?timeframe=">Now</a></li>
<li class="default-filter-option active"><a href="?timeframe=">Now</a></li>
<li class="default-filter-option"><a href="?timeframe=">Now</a></li>
<li class="default-filter-option"><a href="?timeframe=">Now</a></li>
<li class="default-filter-option"><a href="?timeframe=">Now</a></li>
</ul>
</div>
</span>

CSS transition with visibility not working

This is not a bug- you can only transition on ordinal/calculable properties (an easy way of thinking of this is any property with a numeric start and end number value..though there are a few exceptions).

This is because transitions work by calculating keyframes between two values, and producing an animation by extrapolating intermediate amounts.

visibility in this case is a binary setting (visible/hidden), so once the transition duration elapses, the property simply switches state, you see this as a delay- but it can actually be seen as the final keyframe of the transition animation, with the intermediary keyframes not having been calculated (what constitutes the values between hidden/visible? Opacity? Dimension? As it is not explicit, they are not calculated).

opacity is a value setting (0-1), so keyframes can be calculated across the duration provided.

A list of transitionable (animatable) properties can be found here



Related Topics



Leave a reply



Submit