Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CSS transitions do not work when assigned trough JavaScript

I'm having some major headache trying to apply CSS3 transitions to a slideshow trough JavaScript.

Basically the JavaScript gets all of the slides in the slideshow and applies CSS classes to the correct elements to give a nice animated effect, if there is no CSS3 transitions support it will just apply the styles without a transition.

Now, my 'little' problem. All works as expected, all slides get the correct styles, the code runs without bugs (so far). But the specified transitions do not work, even though the correct styles where applied. Also, styles and transitions work when I apply them myself trough the inspector.

Since I couldn't find a logical explanation myself I thought someone here could answer it, pretty please?

I've put together a little example of what the code is right now: http://g2f.nl/38rvma Or use JSfiddle (no images): http://jsfiddle.net/5RgGV/1/

like image 539
Jon Koops Avatar asked Nov 21 '11 10:11

Jon Koops


People also ask

How do you trigger transitions in JavaScript?

To trigger an element's transition, toggle a class name on that element that triggers it. To pause an element's transition, use getComputedStyle and getPropertyValue at the point in the transition you want to pause it. Then set those CSS properties of that element equal to those values you just got.

What triggers CSS transition?

Triggering transitions You can trigger CSS transitions directly with pseudo classes like :hover (activates when the mouse goes over an element), :focus (activates when a user tabs onto an element, or when a user clicks into an input element), or :active (activates when user clicks on the element).

Which delay CSS property is used for transition effect?

The transition-delay property specifies a delay (in seconds) for the transition effect.


2 Answers

To make transition work, three things have to happen.

  1. the element has to have the property explicitly defined, in this case: opacity: 0;
  2. the element must have the transition defined: transition: opacity 2s;
  3. the new property must be set: opacity: 1

If you are assigning 1 and 2 dynamically, like you are in your example, there needs to be a delay before 3 so the browser can process the request. The reason it works when you are debugging it is that you are creating this delay by stepping through it, giving the browser time to process. Give a delay to assigning .target-fadein:

window.setTimeout(function() {   slides[targetIndex].className += " target-fadein"; }, 100);  

Or put .target-fadein-begin into your HTML directly so it's parsed on load and will be ready for the transition.

Adding transition to an element is not what triggers the animation, changing the property does.

// Works document.getElementById('fade1').className += ' fade-in'  // Doesn't work document.getElementById('fade2').className = 'fadeable' document.getElementById('fade2').className += ' fade-in'  // Works document.getElementById('fade3').className = 'fadeable'  window.setTimeout(function() {   document.getElementById('fade3').className += ' fade-in' }, 50)
.fadeable {   opacity: 0; }  .fade-in {   opacity: 1;   transition: opacity 2s; }
<div id="fade1" class="fadeable">fade 1 - works</div> <div id="fade2">fade 2 - doesn't work</div> <div id="fade3">fade 3 - works</div>
like image 196
ThinkingStiff Avatar answered Oct 05 '22 23:10

ThinkingStiff


Trick the layout engine!

function finalizeAndCleanUp (event) {     if (event.propertyName == 'opacity') {         this.style.opacity = '0'         this.removeEventListener('transitionend', finalizeAndCleanUp)     } } element.style.transition = 'opacity 1s' element.style.opacity = '0' element.addEventListener('transitionend', finalizeAndCleanUp) // next line's important but there's no need to store the value element.offsetHeight element.style.opacity = '1' 

As already mentioned, transitions work by interpolating from state A to state B. If your script makes changes in the same function, layout engine cannot separate where state A ends and B begins. Unless you give it a hint.

Since there is no official way to make the hint, you must rely on side effects of some functions. In this case .offsetHeight getter which implicitly makes the layout engine to stop, evaluate and calculate all properties that are set, and return a value. Typically, this should be avoided for performance implications, but in our case this is exactly what's needed: state consolidation.

Cleanup code added for completeness.

like image 35
transistor09 Avatar answered Oct 06 '22 01:10

transistor09