Trying to trigger a CSS transition using JavaScript

Viewed 2307

Playing with JavaScript and CSS transition, I tried to remove a CSS class right after having dynamically inserted a div, using JavaScript and innerHTML.

I'm really surprised to see that the CSS transition associated with the opacity of the blue div is not triggered the way I want (works under Safari, works randomly under Chrome, doesn't work under Firefox Dev Edition). Can someone explain this phenomenon ?

I'm not sure about why it is not working the same way as it does for the red div. Maybe something I don't know about how browsers handle innerHTML ?

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Having fun with JS</title>
    <style>
        .std {
            width:100px;
            height:100px;
            opacity: 1;
            transition: opacity 5s;
        }

        .std--hidden {
            opacity: 0;
        }

        .std--red {
            background-color: red;
        }

        .std--blue {
            background-color: blue;
        }
    </style>
</head>
<body>
<button>click here</button>
<div class='std std--red std--hidden'></div>
<div class='insert-here'>
</div>
<script>
    // let a chance to the browser to trigger a rendering event before the class is toggled
    // see http://stackoverflow.com/a/4575011
    setTimeout(function() {
        // everything works fine for the red div
        document.querySelector('.std--red').classList.toggle('std--hidden');
    }, 0);


    document.querySelector('button').addEventListener('click', function(e) {

        var template = `<div class='std std--blue std--hidden'></div>`;
        document.querySelector('.insert-here').innerHTML = template;

        setTimeout(function() {
            // Why does the CSS transition seems to be triggered randomly ?
            // well more exactly
            // - it works under my Safari
            // - it does not work under my FirefoxDeveloperEdition
            // - it works randomly under my Google Chrome
            document.querySelector('.std--blue').classList.toggle('std--hidden');
        }, 0);

    });
</script>
</body>
</html>

EDIT

So I've just read the CSS transitions specs and found this

This processing of a set of simultaneous style changes is called a style change event. (Implementations typically have a style change event to correspond with their desired screen refresh rate, and when up-to-date computed style or layout information is needed for a script API that depends on it.)

Can this be the explanation somehow ? Does the setTimeout 0 is too fast on some browsers that they don't have time to compute the style differences and thus don't trigger a style change event ? Indeed if use a longer setTimeout (say, ~16.6666, guessing a 1/60 refresh rate...) it seems to work everywhere. Can someone confirm that ?

4 Answers

To trigger a transition you don't really need a class to toggle. This is important since you may not be able to set up classes dynamically to toggle beforehand. In other words you may just need to alter some CSS attributes to trigger the transitions. However yes... you need to satisfy the followings;

  1. In order your <div id="blue"></div> element to animate, it must carry the std class where the transition is defined. So first we should do like blue.classList.add("std");
  2. You need to perform the task asynchronously. Refreshing the DOM asynchronously is best done by the requestAnimationFrame() function.

var blue = document.getElementById("blue");
blue.classList.add("std");
blue.style.backgroundColor = "blue";
blue.style.opacity         = 0;

document.querySelector('button')
        .addEventListener( 'click'
                         , _ => requestAnimationFrame(_ => blue.style.opacity = 1)
                         );
.std { width:100px;
       height:100px;
       opacity: 1;
       transition: opacity 5s;
}
.std--red { background-color: red;}
<button>click here</button>
<div class='std std--red'></div>
<div id="blue"></div>

Related