Skip to main content
Ben Nadel at RIA Unleashed (Nov. 2010) with: Carol Loffelmann and Vicky Ryder
Ben Nadel at RIA Unleashed (Nov. 2010) with: Carol Loffelmann Vicky Ryder

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

By
Published in

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.

Want to use code from this post? Check out the license.

Reader Comments

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel