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
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 puted 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 pute 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 ?
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./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 puted 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 pute 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 ?
Share Improve this question edited Mar 5, 2016 at 9:57 thomas.g asked Mar 4, 2016 at 20:00 thomas.gthomas.g 3,9323 gold badges31 silver badges36 bronze badges 10-
What's the point of using
setTimeout
? It isn't doing anything. Maybe try removing them and see what happens? – Adjit Commented Mar 4, 2016 at 20:07 - Yes it is useful, it queues the code removing the css class after that the browser has first rendered the DOM – thomas.g Commented Mar 4, 2016 at 20:09
-
Try removing the backtick where you inserted the inner HTML elements in your
var template
. and see instead add double quotes "" – Maqk Commented Mar 4, 2016 at 20:11 -
oooh... tha backtick doesn't do anything... you should probably be using double quotes (
"
) for the outside and single quotes ('
) for theclass=
– Adjit Commented Mar 4, 2016 at 20:12 - 1 The backtick corresponds to an ES6 template string. – thomas.g Commented Mar 4, 2016 at 20:13
4 Answers
Reset to default 3I think I've found the answer, see the CSS 3 transition spec:
Since this specification does not define when a style change event occurs, and thus what changes to puted values are considered simultaneous, authors should be aware that changing any of the transition properties a small amount of time after making a change that might transition can result in behavior that varies between implementations, since the changes might be considered simultaneous in some implementations but not others.
I tried to add a little delay to let the browsers notice the style differences and it works consistently. It seems that some browsers are executing a setTimeout 0 really faster than others :-)
setTimeout(function() {
document.querySelector('.std--blue').classList.toggle('std--hidden');
}, 17);
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;
- In order your
<div id="blue"></div>
element to animate, it must carry thestd
class where thetransition
is defined. So first we should do likeblue.classList.add("std");
- 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>
It seems you have to trigger the transition by making a reference to the style. I simply added this line (even into console.log is ok)
document.querySelector('.std.std--blue').style.width = '20';
and here it works: jsfiddle
NB: I am using FirefoxDeveloperEdition.
I have followed this solution:
CSS Transitions do not animate when you add or remove class, It will only animate when you change the CSS properties.
Complete script
<script>
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;
document.querySelector('.std.std--blue').style.width = '20';
setTimeout(function() {
document.querySelector('.std.std--blue').style.width = '100';
document.querySelector('.std--blue').classList.toggle('std--hidden');
}, 0);
});
</script>
You must set a value to the timeout so you give time for the element to be inserted in the DOM (must exist before calling document.querySelector
)
setTimeout(function() { document.querySelector('.std--blue').classList.toggle('std--hidden')},100);
you don't need the first timeout
onload = function() {
document.querySelector('.std--red').classList.toggle('std--hidden');
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() { document.querySelector('.std--blue').classList.toggle('std--hidden')},100);
});
}
.std {
width:100px;
height:100px;
opacity: 1;
transition: opacity 5s;
}
.std--hidden {
opacity: 0;
}
.std--red {
background-color: red;
}
.std--blue {
background-color: blue;
}
<button>click here</button>
<div class='std std--red std--hidden'></div>
<div class='insert-here'>
</div>