Skip to main content
Ben Nadel at InVision In Real Life (IRL) 2019 (Phoenix, AZ) with: Jeremiah Lee
Ben Nadel at InVision In Real Life (IRL) 2019 (Phoenix, AZ) with: Jeremiah Lee

Don't Alter The DOM (Too Much) During The $destroy Event In AngularJS

By
Published in

Lately, I've been digging into the ngAnimate module a lot. It's an awesome part of the AngularJS framework that makes elemental transitions quite easy to implement, even with an extremely dynamic document object model (DOM). But, all of these transitions have gotten me thinking about the state of the DOM as it exists mid-transition. And, while this probably goes without saying, I thought I would make something explicit: don't alter the DOM (too much) during the $destroy event in AngularJS.

Run this demo in my JavaScript Demos project on GitHub.

This is a "suggestion", not a "decree." If you have things - like jQuery plugins - that you need to teardown during the $destroy event, please do that; memory leaks are a bad thing. But, generally speaking, don't alter the DOM too much during the $destroy event. With the addition of ngAnimate transitions, doing so can lead to odd visual effects.

To see a quick demonstration of what I mean, I've created an AngularJS directive that will conditionally clear the element content during the destroy event. The following code has two dynamic Div elements - one gets cleared, one does not. will

<!doctype html>
<html ng-app="Demo">
<head>
	<meta charset="utf-8" />

	<title>
		Don't Alter The DOM (Too Much) During The $destroy Event In AngularJS
	</title>

	<link rel="stylesheet" type="text/css" href="./demo.css"><link>
</head>
<body ng-controller="AppController">

	<h1>
		Don't Alter The DOM (Too Much) During The $destroy Event In AngularJS
	</h1>

	<p>
		<a ng-click="toggleViews()">Toggle View(s)</a>
		&mdash;
		{{ ( isShowingViews ? "Visible" : "Hidden" ) }}
	</p>

	<div class="views">

		<!-- When this view is destroyed, we'll clear the content. -->
		<div ng-if="isShowingViews" bn-view="true" class="view view-a">

			<h2>
				This is a View!
			</h2>

			<p>
				How cool!
			</p>

		</div>

		<!-- When this view is destroyed, we'll leave the content alone. -->
		<div ng-if="isShowingViews" bn-view="false" class="view view-b">

			<h2>
				This is another View!
			</h2>

			<p>
				Tote's neat!
			</p>

		</div>

	</div>


	<!-- Load scripts. -->
	<script type="text/javascript" src="../../vendor/angularjs/angular-1.4.5.min.js"></script>
	<script type="text/javascript" src="../../vendor/angularjs/angular-animate-1.4.5.min.js"></script>
	<script type="text/javascript">

		// Create an application module for our demo.
		var app = angular.module( "Demo", [ "ngAnimate" ] );


		// --------------------------------------------------------------------------- //
		// --------------------------------------------------------------------------- //


		// I control the root of the application.
		angular.module( "Demo" ).controller(
			"AppController",
			function AppController( $scope ) {

				$scope.isShowingViews = true;


				// ---
				// PUBLIC METHODS.
				// ---


				// I show or hide the views depending on their current visibility.
				$scope.toggleViews = function() {

					$scope.isShowingViews = ! $scope.isShowingViews;

				};

			}
		);


		// --------------------------------------------------------------------------- //
		// --------------------------------------------------------------------------- //


		// I conditionally clear the current element during the $destroy event.
		angular.module( "Demo" ).directive(
			"bnView",
			function bnViewDirective() {

				// Return the directive configuration object.
				return({
					link: link,
					restrict: "A"
				});


				// I bind the JavaScript events to the view-model.
				function link( scope, element, attributes ) {

					if ( attributes.bnView !== 'true' ) {

						return;

					}

					// When the scope is destroyed, let's clear the content of the
					// element. This is a very silly example; but, the extreme case
					// readily demonstrates why you don't want to alter the DOM [too
					// much] during the $destroy event.
					// --
					// NOTE: It's OK to clean up transient elements that may relate to
					// things like jQuery plugins. This is more of a suggestion than a
					// commandment.
					scope.$on(
						"$destroy",
						function handleDestroy() {

							element.empty();

						}
					);

				}

			}
		);

	</script>

</body>
</html>

As you can see, when both of the views are removed from the page, one of them (the first one, bnView=true) will clear its own content. If these changes happen instantly, you don't see anything. But, if these views are transitioned off of the page using opacity, the DOM manipulation becomes immediately obvious:

Don't alter the DOM (too much) during the $destroy event in AngularJS.

Chances are, you aren't doing this anyway. But, I just wanted to take a minute to demonstrate why this is something that should be avoided. If nothing else, I now have a strong, more explicit mental model of AngularJS and ngAnimate.

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