5

I have a menu that I would like to fade in with javascript. I want it to transition from display: none and opacity: 0 to display: flex and opacity: 1. But when I set opacity to 1 using javascript, it doesn't transition, and instead abruptly snaps to 1, whereas If I do not set display to none, it gracefully transitions. I want to use display: none because before the menu appears I need to be able to catch mouse movement on a canvas in the background. I have made a codepen to demonstrate this here.

Note: I want to be able to fade out too using Javascript

I have also taken a look at this question, but the first suggested answer isn't able to fade out.

Thanks!

text = document.getElementById("text");
window.setTimeout((function () {
  text.style.display = "flex";
  text.style.opacity = "1";
}), 2000)
#text {
  display: none;
  opacity: 0;
  width: 500px;
  height: 100px;
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  font-size: 3rem;
  color: white;
  align-items: center;
  justify-content: center;
  transition-duration: 2s;
  background-color: black;
  border-radius: 5px;
}
<div id="text">Testing Testing 123</div>
  • 2
    Browser will not recognize a `display: none` element's attributes. Transition will simply stop. You might want to look into `visibility` or use just opacity, then use `display: none` after the animation has completed. – Dom Sep 21 '20 at 22:34
  • I have a canvas in the background that needs to be actively getting user input while the menu is not visible. I believe visibility: none would get in the way of that. – Impossible Reality Sep 21 '20 at 23:11

4 Answers4

4

As bizarre as it may seem, the answer is to add a line to your code as follows:

window.setTimeout((function () {
  text.style.display = "flex";
  document.body.offsetHeight;  // Yes, this line!
  text.style.opacity = "1";
}), 2000);

There's nothing special about this line other than that it performs a 'read' of data within your page (any operation that reads data from the DOM would work). What this does is force the browser to layout (or reflow) the page. This is important because, in general, if you carry out a series of 'write' operations - e.g. adding an element or setting it's style, the browser will batch these up and perform them all at once. This means that when you set the element's opacity to 0, and then to 1, the browser batches up these operations and carries them out together before reflowing the page, and thus there is no animation. By inserting a write operation in between, the browser is able to animate from the state of the element where it is transparent to the state where it is fully opaque.

Making it disappear is a little different:

text = document.getElementById("text");

window.setTimeout((function () {
  text.style.display = "flex"; // write operation
  document.body.offsetHeight; // read operation which forces reflow

  text.addEventListener('transitionend', function listener1() {
    text.removeEventListener('transitionend', listener1);
    
    text.addEventListener('transitionend', function listener2() {
      text.removeEventListener('transitionend', listener2);
      text.style.display = 'none'; // remove text
    });
    
    window.setTimeout(function () {
      text.style.opacity = 0.1; // hide text
    }, 1000);
  });
  
  text.style.opacity = 1; // write operation - show text
  
}), 2000);

It's best to wait for the previous transition to complete before starting a new one. It's also good practise to remove the event listeners after the event has fired. You have to wait for the transition to complete before removing the element from the DOM. There is no need to carry out a read operation before setting the style that triggers an animation because the page has already been laid out with the opacity set to 1. I have set opacity to 0.1 so that you can see that the element actually disappears.

You can see a JFiddle here.

Richard Hunter
  • 1,933
  • 2
  • 15
  • 22
0

No JS needed...

EDIT: Removed JS that was being used for positioning.

You can add an animation css rule and then add keyframes within your CSS that points the animation to your opacity. Name it something like fadeIn and then add an ease and duration you wish to have the animation work. Then add @keyframes fadeIn in your css and set the key frames for the opacity to run through a percentage of 0% { opacity: 0; } then 100% { opacity: 1; }. This will add the fade in ease I think you are looking for.

EDIT: If you're looking to have it fade in and then fade out after a set length of time, tweak the keyframes and animation time setting so that your animation plays out as you wish. See my updated snipit...

Mozilla @keyframes Documentation

#text {
  animation: fadeIn-fadeOut ease 5s;
  display: flex;
  opacity: 0;
  width: 500px;
  height: 100px;
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  font-size: 3rem;
  color: white;
  align-items: center;
  justify-content: center;
  background-color: black;
  border-radius: 5px;
}

@keyframes fadeIn-fadeOut {
  10% {
    opacity: 0;
  }
  20% {    
    opacity: 1;
  }
  90% {
    opacity: 1;
  }
  100% {
    opacity: 0;
  }
}
<div id="container">
  <div id="text">Testing Testing 123</div>
</div>
dale landry
  • 7,831
  • 2
  • 16
  • 28
  • Hi! Sorry if I was unclear, but I also need to be able to fade out using javascript. I have edited my post, so that it is more clear. – Impossible Reality Sep 21 '20 at 23:09
  • @wickedtree see the update. I am not sure how you want the css to fade back out, but if it is time related, it can be done using keyframes as well. Simply reset your time in animation and set *`two`* inner keyframes to trigger the opacity in, then the last frame will trigger the `opacity: 0` This can be tweaked many ways to get different effects with regards to animations and keyframes... – dale landry Sep 21 '20 at 23:25
  • No, its not time related. It is triggered by javascript after loading all of the assets. Thanks though for the suggestion! – Impossible Reality Sep 21 '20 at 23:33
  • Why does this answer say "No JS needed" when the solution uses JS? – Hashim Aziz Jun 11 '22 at 23:46
0

Do not use display: none for elements you want to fade-in. display: none will remove the element from your display. It won't have your css animation applied when it reappears.

Consequently you would have to use JavaScript for the animation. Keep your animation/display logic in your CSS.

There is an old CSS trick where you hide absolute or fixed elements offscreen. using the left property.

const text = document.getElementById("text");
window.setTimeout( function() {
  text.classList.remove('hidden');
}, 1000);

window.setTimeout( function() {
  text.classList.add('hidden');
}, 5000)
#text {
  width: 500px;
  height: 100px;
  display: flex;
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  font-size: 3rem;
  color: white;
  align-items: center;
  justify-content: center;
  background-color: black;
  border-radius: 5px;
  opacity: 1;
  transition: opacity 500ms;
}
#text.hidden {
  left: -500000px;
  opacity: 0;
  transition: opacity 500ms, left 0ms 500s;
}
<div id="text" class='hidden'>Testing Testing 123</div>
Rob Monhemius
  • 4,822
  • 2
  • 17
  • 49
  • As Dom mentioned earlier, you could also look into `visibility`. `visibility` has implications for screen-readers : https://www.w3schools.com/CSSref/pr_class_visibility.asp – Rob Monhemius Sep 21 '20 at 23:36
0

My solution was to set a 1ms timeOut between the display and the opacity. I think all of these solutions are a bit tacky. I'm looking for a better solution, but I guess there is none.

agiopnl
  • 1,190
  • 9
  • 18