1

Update 2019-12-18 with better solution

See other SO question with updated solution Trigger CSS transition on appended element

Short version: wrap the adding of a CSS animation class in a JS block that forces the browser to re-render the flow and not optimize it into a single call.

# CSS animation class
.visible {
  ...styles to change transparency from 0 -> 1
  ...styles to transform(scale) from 0.8 -> 1.0
}

# JS code
requestAnimationFrame(() => {
  this.element.classList.add("visible")
})

Previously I had to do something like this:

$element.hide().show()
$element.addClass("visible")

Original question

I'm building an overlay (background for modals or dialog boxes) and I want it to fade in when I create the element. I do the animation by adding/removing a .visible class to the element using CSS3 transitions.

# SASS styles
.overlay {
  background-color: rgba(0, 0, 0, 0.6);
  cursor: pointer;
  height: 100%;
  left: 0;
  position: fixed;
  pointer-events: none;
  top: 0;
  width: 100%;
  will-change: opacity;
  @include transparency(0);
  @include transition(opacity 0.3s cubic-bezier(0, 0, 0.3, 1));

  &.visible {
    pointer-events: auto;
    @include transparency(1.0);
  }
}

When the overlay element already exists on the DOM, everything works just fine:

$(".overlay").addClass("visible") # => does animation as expected...

However, when I CREATE the element and THEN try to animate it, it does not:

# JavasScript using jQuery
tag = $("<div class='overlay'></div>")
$("body").append(tag)
tag.addClass("visible")

I understand this is because the JavaScript is creating and adding the class "instantly", so what I have to do is this:

tag = $("<div class='overlay'></div>")
$("body").append(tag)
tag.hide()
tag.show()
tag.addClass("visible")

By "hiding" and then "showing" the element, it has enough time for the add class to animate the element.

Question This seems pretty hacky to show/hide an element so I can then animate it via CSS transitions. Is there a cleaner way of implement this?

Dan L
  • 4,319
  • 5
  • 41
  • 74

1 Answers1

0

"Cleaner" is in the eye of the beholder, but one simple way is to wait a tick before you add the visible class:

$("body").append(tag);
setTimeout(function () { 
  tag.addClass("visible");
}, 0);

That'll wait until the appending is complete before adding the class. You might need to adjust the 0 value higher.

Heretic Monkey
  • 11,687
  • 7
  • 53
  • 122
  • Agree that "cleaner" is often subjective. I don't really "like" using a global Timeout a whole lot, but it is just as clean as a a hacky `.hide().show()` method. Wish HTML/CSS had some attribute/property I could set that would allow me to animate on a given command instead of applying the classes to it...I suspect something like that will happen in the next few years as these kinds of situations become more common. – Dan L Nov 04 '16 at 11:18