En jouant avec la transition JavaScript et CSS, j'ai essayé de supprimer une classe CSS juste après avoir inséré dynamiquement un div, en utilisant JavaScript et innerHTML.

Je suis vraiment surpris de voir que la transition CSS associée à l'opacité du div bleu ne se déclenche pas comme je le souhaite (fonctionne sous Safari, fonctionne au hasard sous Chrome, ne fonctionne pas sous Firefox Dev Edition). Quelqu'un peut-il expliquer ce phénomène?

Je ne sais pas pourquoi cela ne fonctionne pas de la même manière que pour le div rouge. Peut-être quelque chose que je ne sais pas sur la façon dont les navigateurs gèrent 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>

ÉDITER

Je viens donc de lire les Spécifications des transitions CSS et j'ai trouvé ceci

Ce traitement d'un ensemble de changements de style simultanés est appelé un événement de changement de style. (Les implémentations ont généralement un événement de changement de style pour correspondre à leur taux de rafraîchissement d'écran souhaité, et lorsque des informations de style ou de mise en page calculées à jour sont nécessaires pour une API de script qui en dépend.)

Cela peut-il être l'explication en quelque sorte? Le setTimeout 0 est-il trop rapide sur certains navigateurs pour qu'ils n'aient pas le temps de calculer les différences de style et ne déclenchent donc pas d'événement de changement de style? En effet, si vous utilisez un setTimeout plus long (disons, ~ 16.6666, en supposant un taux de rafraîchissement de 1/60 ...) il semble fonctionner partout. Quelqu'un peut-il confirmer cela?

3
thomas.g 4 mars 2016 à 23:00

3 réponses

Meilleure réponse

Je pense avoir trouvé la réponse, voir les spécification de transition CSS 3 :

Étant donné que cette spécification ne définit pas le moment où un événement de changement de style se produit, et donc les changements apportés aux valeurs calculées sont considérés comme simultanés, les auteurs doivent être conscients que la modification de l'une des propriétés de transition un petit laps de temps après avoir effectué une modification susceptible de provoquer une transition peut entraîner comportement qui varie entre les implémentations, car les modifications peuvent être considérées comme simultanées dans certaines implémentations mais pas dans d'autres.

J'ai essayé d'ajouter un peu de retard pour permettre aux navigateurs de remarquer les différences de style et cela fonctionne de manière cohérente. Il semble que certains navigateurs exécutent un setTimeout 0 vraiment plus rapidement que d'autres :-)

    setTimeout(function() {
        document.querySelector('.std--blue').classList.toggle('std--hidden');
    }, 17);
1
thomas.g 5 mars 2016 à 09:57

Il semble que vous deviez déclencher la transition en faisant référence au style. J'ai simplement ajouté cette ligne (même dans console.log est ok)

document.querySelector('.std.std--blue').style.width = '20';

Et ici ça marche: jsfiddle

NB: j'utilise FirefoxDeveloperEdition.

J'ai suivi cette solution:

Les transitions CSS ne s'animent pas lorsque vous ajoutez ou supprimez une classe, elles ne s'animent que lorsque vous modifiez les propriétés CSS.

Script complet

<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>
0
Community 23 mai 2017 à 12:23

Vous devez définir une valeur pour le délai d'expiration afin de donner du temps pour que l'élément soit inséré dans le DOM (doit exister avant d'appeler document.querySelector)

setTimeout(function() { document.querySelector('.std--blue').classList.toggle('std--hidden')},100);

Vous n'avez pas besoin du premier 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>
-1
nodws 4 mars 2016 à 20:33