Angular.js: ng-if behaves weirdly with CSS infinite keyframe animations

Created on 3 Jan 2014  路  3Comments  路  Source: angular/angular.js

Related to https://github.com/angular/angular.js/issues/4818

See the following Plunker: http://plnkr.co/edit/i1DJ6ejtuHVFHty9jxfe?p=preview

The element <div class="box" ng-if="showBox"> has a 5-second infinite animation for a permanently cycling background. When the value of showBox is toggled, the element's animation starts over again, and it isn't removed from the DOM until the entire 5 seconds has passed - despite no enter/leave animations being defined.

Even more problematic: if you toggle the value multiple times before the animation is over (click the checkbox several times), multiple copies of the element are displayed simultaneously.

ngAnimate moderate bug

Most helpful comment

The problem is that ngAnimate thinks your infinite animation is there for all of the animation events used (like enter, leave and so on). And since it is an infinite animation then the CSS events never get fired until the final countdown has passed which is after 7.5 seconds (5 x 1.5 == 7.5).

You'll need to tell ng-animate not to animate the infinite background animation by overriding the .ng-animate CSS class on the element and cancelling out the keyframe animations.

http://plnkr.co/edit/fq7ZEO7tDnqLmavHoBfQ?p=preview

.box.ng-animate {
  -webkit-animation: none 0s;
  animation: none 0s;
}

Unfortunately detecting styles and CSS classes in JavaScript is highly limited. With earlier versions of AngularJS we had the rule that you can only define the CSS animation/transition on the animation property (like .ng-enter, .ng-leave and so on), but that caused more issues than it solved. So in this case you need to do the opposite.

PS. All you need is the -webkit- prefix and the standard animation properties since all other vendors dropped their prefixes.

All 3 comments

The problem is that ngAnimate thinks your infinite animation is there for all of the animation events used (like enter, leave and so on). And since it is an infinite animation then the CSS events never get fired until the final countdown has passed which is after 7.5 seconds (5 x 1.5 == 7.5).

You'll need to tell ng-animate not to animate the infinite background animation by overriding the .ng-animate CSS class on the element and cancelling out the keyframe animations.

http://plnkr.co/edit/fq7ZEO7tDnqLmavHoBfQ?p=preview

.box.ng-animate {
  -webkit-animation: none 0s;
  animation: none 0s;
}

Unfortunately detecting styles and CSS classes in JavaScript is highly limited. With earlier versions of AngularJS we had the rule that you can only define the CSS animation/transition on the animation property (like .ng-enter, .ng-leave and so on), but that caused more issues than it solved. So in this case you need to do the opposite.

PS. All you need is the -webkit- prefix and the standard animation properties since all other vendors dropped their prefixes.

Cool, thanks for the workaround.

The multiple-copies-at-once thing still seems like a bug to me: I write my code on the assumption that an element using ng-if is either present once or not present at all, and I'd expect the framework to prevent it from breaking so easily due to an inadvertent noob mistake. Please correct me if I've misunderstood how ng-if (or ng-switch) is intended to function.

The multiple-copies was there because the animations were still going on. And $animate has a catch-all animation for keyframes/transitions that occurs after your duration * 1.5 expires. That's why they disappear after a moment.

Your ng-if code is correct. And yes it is only there when true. But the internals make a new copy of the element each time the expression goes true again. But since the animation is hanging on the older element then it hasn't been deleted yet until the catch all animation comes in.

And ng-switch and ng-if operate in the same way. Only ng-switch has multiple elements instead of only one.

Was this page helpful?
0 / 5 - 0 ratings