Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
Ben Nadel at the New York ColdFusion User Group (Jun. 2008) with: Ye Wang
Ben Nadel at the New York ColdFusion User Group (Jun. 2008) with: Ye Wang

Child Animations Have To Inherit Transition-Property In AngularJS 1.2 And 1.3

By Ben Nadel on

A couple of weeks ago, I demonstrated that animating static child elements, in AngularJS 1.4, involved the use of the "transition-delay" CSS property. Unfortunately, when I went to apply this in an AngularJS 1.2 application, I discovered that the use of "transition-delay" was actually part of the 1.4 rewrite. AngularJS 1.2 (and 1.3 for that matter) use a different means of blocking animations during the configuration phase. After a couple of hours adding "debugger" and "console.log()" statements, I finally figured out what was going on. And, unfortunately, animating child elements in AngularJS 1.2 requires a slightly less elegant approach.


 
 
 

 
 
 
 
 

Run this demo in my JavaScript Demos project on GitHub.

The ngAnimate code is rather complex and hard to follow. So, I don't pretent to really understand what's going on - it's written by people much smarter than myself. But, from my best estimate, AngularJS 1.2 (or rather, ngAnimate 1.2) blocks configuration-phase animations by adding this inline style to the transitioning element:

transition-property: none ;

Through the use of a "debugger;" statement, we can see this in the Chrome dev tools:


 
 
 

 
 AngularJS 1.2 and ngAnimate inject transition-property: none when blocking configuration-phase animations. 
 
 
 

This affects the transitioning element but has no bearing on the child elements. As such, we have to update our AngularJS 1.2 CSS to inherit "transition-property." That way, when the parent is being "blocked", the children will inherit that block. The problem that this presents is that the child element cannot defined its own transition properties. Which means, the parent has to define both its own transition properties as well as those required by any of the child elements.

So, for example, if a parent element was going to transition "opacity" and one of the children was going to contextually transition "top", the parent element would have to define:

transition-property: opacity, top ;

This way, the child behavior can be overridden by the inline "none" value while still inheriting the appropriate property once the transition-block is removed.

To see this in action, I've refactored my previous transition-delay demo to use AngularJS 1.2 (or 1.3). In this demo, I have three Divs that will "enter" and "leave" using an opacity transition. And, as they do, the child Span elements will transition their margin-top values.

  • <!doctype html>
  • <html ng-app="Demo">
  • <head>
  • <meta charset="utf-8" />
  •  
  • <title>
  • Child Animations Have To Inherit Transition-Property In AngularJS 1.2 And 1.3
  • </title>
  •  
  • <link rel="stylesheet" type="text/css" href="./demo.css"></link>
  • </head>
  • <body ng-controller="AppController">
  •  
  • <h1>
  • Child Animations Have To Inherit Transition-Property In AngularJS 1.2 And 1.3
  • </h1>
  •  
  • <p>
  • <a ng-click="toggleViews()">Toggle Views</a>
  • </p>
  •  
  • <!--
  • NOTE: The following three DIV elements all use the same exact styles. The only
  • difference between "a", "b", and "c" is the LEFT style that is being used. As
  • the DIV performs its ng-enter and ng-leave animations, we are going to animate
  • the SPAN element as well, based on the contextual animation class.
  • -->
  •  
  • <div ng-if="isShowingView" class="view view-a">
  •  
  • <span>View A</span>
  •  
  • </div>
  •  
  • <div ng-if="isShowingView" class="view view-b">
  •  
  • <span>View B</span>
  •  
  • </div>
  •  
  • <div ng-if="isShowingView" class="view view-c">
  •  
  • <span>View C</span>
  •  
  • </div>
  •  
  •  
  • <!-- Load scripts (1.2.22 or 1.3.15). -->
  • <script type="text/javascript" src="../../vendor/angularjs/angular-1.2.22.js"></script>
  • <script type="text/javascript" src="../../vendor/angularjs/angular-animate-1.2.22.js"></script>
  • <script type="text/javascript">
  •  
  • // Create an application module for our demo.
  • angular.module( "Demo", [ "ngAnimate" ] );
  •  
  •  
  • // --------------------------------------------------------------------------- //
  • // --------------------------------------------------------------------------- //
  •  
  •  
  • // I control the root of the application.
  • angular.module( "Demo" ).controller(
  • "AppController",
  • function AppController( $scope ) {
  •  
  • $scope.isShowingView = false;
  •  
  •  
  • // ---
  • // PUBLIC METHODS.
  • // ---
  •  
  •  
  • // I toggle the existence of the animated views.
  • $scope.toggleViews = function() {
  •  
  • $scope.isShowingView = ! $scope.isShowingView;
  •  
  • };
  •  
  • }
  • );
  •  
  • </script>
  •  
  • </body>
  • </html>

And, here's the CSS that controls the animation. What you'll see is that the "view" element has to define both the "opacity" and the "margin-top" transition-property values. This way, the dynamic elements can use "opacity" while the static child elements can use "margin-top." And, to get that to work, the contextual CSS needs to inherit the transition-property value provided by the parent:

  • a[ ng-click ] {
  • color: red ;
  • cursor: pointer ;
  • text-decoration: underline ;
  • user-select: none ;
  • -moz-user-select: none ;
  • -webkit-user-select: none ;
  • }
  •  
  • div.view {
  • border: 2px solid #FF0099 ;
  • border-radius: 4px 4px 4px 4px ;
  • height: 200px ;
  • left: 40px ;
  • overflow: hidden ;
  • position: fixed ;
  • top: 130px ;
  • width: 288px ;
  • }
  •  
  • div.view.view-a {
  • left: 40px ;
  • }
  •  
  • div.view.view-b {
  • left: 370px ;
  • }
  •  
  • div.view.view-c {
  • left: 700px ;
  • }
  •  
  • div.view span {
  • border: 1px dotted #AAAAAA ;
  • border-radius: 4px 4px 4px 4px ;
  • display: block ;
  • margin: 20px 20px 0px 20px ;
  • padding: 10px 10px 10px 10px ;
  • text-align: center ;
  • }
  •  
  •  
  • /* Container [ng-if] animation configuration. */
  •  
  • div.view.ng-enter,
  • div.view.ng-leave {
  • transition-duration: 2s ;
  • transition-timing-function: ease ;
  •  
  • /*
  • Due to the way that AngularJS 1.2 and 1.3 block transitions during the
  • "setup phase" of the animation, we have to use inheritance for the
  • transition properties. As such, the "parent" has to define more properties
  • than it [the parent] will actually transition. It has to define both its own
  • properties as well as those used by its children.
  • */
  • transition-property: opacity, margin-top ;
  • }
  •  
  • div.view.ng-enter {
  • opacity: 0.0 ;
  • }
  •  
  • div.view.ng-enter-active {
  • opacity: 1.0 ;
  • }
  •  
  • div.view.ng-leave {
  • opacity: 1.0 ;
  • }
  •  
  • div.view.ng-leave-active {
  • opacity: 0.0 ;
  • }
  •  
  •  
  • /* Nested element animation configuration. */
  •  
  • div.view.ng-enter span,
  • div.view.ng-leave span {
  • transition-delay: inherit ;
  • transition-duration: inherit ;
  • transition-timing-function: inherit ;
  •  
  • /*
  • In AngularJS 1.2 and 1.3, animations are blocked during the configuration phase
  • using an inline style, "transition-property: none". In order to prevent the child
  • animations from firing too soon, the children have to inherit this inline style,
  • which means that the children have to inherit the transition-property from its
  • parent chain.
  • */
  • transition-property: inherit ;
  • }
  •  
  • div.view.ng-enter span {
  • margin-top: 200px ;
  • }
  •  
  • div.view.ng-enter-active span {
  • margin-top: 20px ;
  • }
  •  
  • div.view.ng-leave span {
  • margin-top: 20px ;
  • }
  •  
  • div.view.ng-leave-active span {
  • margin-top: 200px ;
  • }

As you can see, the static child elements, in AngularJS 1.2, basically has to inherit all transition-related values.

AngularJS 1.4 makes this a bit easier because it's using transition-delay to block configuration phase transitions. That allows each child element to define its own transition properties. However, before the AngularJS 1.4 rewrite, we have to fallback to inheriting transition-properties in order to apply the configuration-phase transition blocking to the child elements.




Reader Comments

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.