Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
Ben Nadel at cf.Objective() 2014 (Bloomington, MN) with: Shawn Grigson
Ben Nadel at cf.Objective() 2014 (Bloomington, MN) with: Shawn Grigson@shawngrig )

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

By Ben Nadel on

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.




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.