8

I have a very strange issue. I'm loading articles from JSON in jQuery and as they load, I'd like to add a class of 'animate' to each dynamic element.

$.each(jsonArticles, function (i, article) {

    var $articleHTML = $(
    '<article class="article">' +
        '<a href="' + jsonObject.filmLink + article.reviewLink + '"><img src="' + jsonObject.imagePath + article.reviewImage + '" alt=""></a>' +
        '<h1><a href="' + jsonObject.filmLink + article.reviewLink + '">' + article.reviewTitle + '</a></h1>' +
        '<p><a href="' + jsonObject.filmLink + article.reviewLink + '">' + article.reviewSummary + '</a></p>' +
    '</article>');

    $articles
        .append($articleHTML)
            .find("article")
                .addClass("animate");

});

All of this works great and checking in Firebug reveals that the class is successfully added to each article tag.

However, when trying to use a CSS transition on the article for the class that's added, it does not animate, but instead skips straight to the final style (opacity: 1).

.article { 

opacity: 0;

    -webkit-transition: all 0.5s ease;
    -moz-transition: all 0.5s ease;
    -o-transition: all 0.5s ease;
    transition: all 0.5s ease;

}

.article.animate { 

    opacity: 1;

}

The animation doesn't happen, but the class is added and the article is successfully set to opacity: 1. It shows up instantly.

Anyone have any ideas about this? I cannot figure this one out at all.

On another point, which is rather interesting...if I change the .animate class to have a :hover, then the articles won't show until I hover and the animation does work. Why it would work for hover and not when it's simply added immediately, seems strange to me.

.article.animate:hover { 

    opacity: 1;

}

I'd appreciate any input.

Thanks, Mikey.


Live Demo: http://jsfiddle.net/Pz5CD/ Notice how the articles just pop in at 100% opacity. No animation is seen.

Michael Giovanni Pumo
  • 14,338
  • 18
  • 91
  • 140

2 Answers2

9

Update:

It turns out the OP wants to fade in each element sequentially, which is beyond the scope of the original question. I'll leave my answer here as an answer to the original question.

CSS animation won't trigger on addClass in jQuery

The issue is that your new html is added to the page and the animate class is added before the css for that html has been applied. The browser will skip ahead like that for the sake of efficiency. For example, if you added a class, then removed it, and repeated that process a hundred times, there wouldn't be a visual difference. It would have just skipped to the result. For this reason, you have to force a redraw on the element so that all previous styles have applied before adding the class. I wrote a function to handle this that should work in every circumstance on every browser, though there's no way to guarantee the behavior of a reDraw. It probably will always work and it's nice to have!

Live demo here (click). You can tell the reDraw is making the difference by commenting it out and just leaving the addClass().

$('body').append($articleHTML);
$a = $('body').find("article");
reDraw($a).then(function() {
  $a.addClass("animate");
});

function reDraw($element) {
  var deferred = new $.Deferred();
  setTimeout(function() {
    var h = $element[0].offsetHeight;
    var s = $element[0].getComputedStyle;
    deferred.resolve();
  },0);
  return deferred.promise();
}

The best way to force a redraw is to either access the offsetHeight or getComputedStyle of an element. However, there have been cases where those have failed for force a redraw on certain mobile devices. To add some extra encouragement for a redraw, I added a setTimeout as well. Even a time of 0 on the timeout will work, but it throws off the call stack, so I use a promise to ensure the next operation (adding your class) will happen after the redraw. That just means you'll use the syntax I demonstrated above to add the class - redraw($element).then(function() { //your code

For fun, I made a little demo of flipping classes with and without reDraw. http://jsbin.com/EjArIrik/1/edit

m59
  • 43,214
  • 14
  • 119
  • 136
  • @MichaelGiovanniPumo I changed my answer. It's a redraw issue, but setTimeout alone isn't necessarily the best way to go. – m59 Jan 05 '14 at 20:54
  • I setup a fiddle here, illustrating my issue: http://jsfiddle.net/Pz5CD/ I want each item to fade in one after the other, in a sequence. Instead, they just pop into view and I can't see why. – Michael Giovanni Pumo Jan 05 '14 at 21:33
  • @MichaelGiovanniPumo Here's a quick and messy thing - none of it has to do with css http://jsfiddle.net/Au3Vt/ – m59 Jan 05 '14 at 21:38
  • OR instead of using all this js, you could just use 2 keyframes that use the same animations, with different names, and toggle back and forth between them. Then, the browser will see it as a completely and preform it again. – kennsorr Oct 15 '18 at 16:16
  • @kennsorr - That is an interesting suggestion, perhaps it would be good in an answer. – Travis J Feb 12 '19 at 06:09
5

You need to add the class to the element after it is rendered to the dom, a set timout might work

setTimeout(function(){
    $articleHTML.addClass("animate");
}, i * 500 );

http://jsfiddle.net/Pz5CD/1/

Musa
  • 96,336
  • 17
  • 118
  • 137
  • 1
    This works in the sense that the elements have each class added in sequence, but it does NOT animate. It simply goes from 0 opacity to 1 immediately. It's really strange. – Michael Giovanni Pumo Jan 05 '14 at 20:33
  • Whilst your demo works, I think you really need this in the each loop to see my issue. There's something about adding dynamic HTML elements and adding the class thereafter, in the loop. – Michael Giovanni Pumo Jan 05 '14 at 21:18
  • @MichaelGiovanniPumo provide a live demo reproducing your issue for us to see. – Musa Jan 05 '14 at 21:22
  • see this fiddle here: http://jsfiddle.net/Pz5CD/ Notice how the articles simply 'pop' into opacity 1 instead of fading in as per the CSS? – Michael Giovanni Pumo Jan 05 '14 at 21:23
  • @m59 Yes, the fade works, but I'm after having each one fade sequentially. 1, fades, then 2 fades...etc. Is this possible? – Michael Giovanni Pumo Jan 05 '14 at 21:29
  • @MichaelGiovanniPumo Musa's approach is the way to go. Unfortunately, this makes my reDraw function irrelevant heheh. The issue now is that the correct answer has nothing to do with the post itself... – m59 Jan 05 '14 at 21:47
  • I updated my answer to clarify what issue I'm answering - if you want to update your question to something like "How to fade in elements sequentially with addClass()`, that would be helpful to future viewers. – m59 Jan 05 '14 at 21:51