How to watch the DOM for changes AngularJS

30,298

Solution 1

It's a bad idea, but I'll humor you.

So as I've stated in my comments above, it's a bad idea to do what you're trying to do. That said, if you still want to go this way, you can $watch just about anything by passing a function in as the first parameter.

The code below will check to see if the HTML inside the <body> tag has changed. Please note, that this is a HORRIBLE idea.

$scope.$watch(function () {
   return document.body.innerHTML;
}, function(val) {
   //TODO: write code here, slit wrists, etc. etc.
});

The watch above will fire anytime ANYTHING in the HTML changes.

  • when a value changes in an input
  • when the selection changes on a dropdown
  • when an attribute changes in any element in the html.
  • etc.

Additional Info: As of right now, in a whole lot of browsers, there's not really a good way to monitor for new DOM elements which have been loaded. The best way is still to trigger something at the moment the new DOM elements were added.

Solution 2

As mentioned by Steinway Wu, MutationObserver is the way to go. Here is a snippet :

.directive('myDirective', function() {
    return {
        ...
        link: function(scope, element, attrs) {
            var observer = new MutationObserver(function(mutations) {
                // your code here ...
            });
            observer.observe(element[0], {
                childList: true,
                subtree: true
            });
        }
    };
});

Solution 3

I created a small utility service

here is the code

in GenericService.js we have this

angular.module('GenericServiceModule', [])
    .factory('$utilsService', function () {

        var utilService = {};

        utilService.observeDomChange = function (domElement, doThisOnChange, observationOptions) {
            var defaultOptions = {
                // attributes: true, // attribute changes will be observed | on add/remove/change attributes
                // attributeOldValue: true, // will show oldValue of attribute | on add/remove/change attributes | default: null
                // we need this because in .find() sizzle modifies id temporarily, https://github.com/jquery/jquery/issues/2620
                //attributeFilter: ['style'], // filter for attributes | array of attributes that should be observed, in this case only style

                // characterData: true, // data changes will be observed | on add/remove/change characterData
                // characterDataOldValue: true, // will show OldValue of characterData | on add/remove/change characterData | default: null

                childList: true, // target childs will be observed | on add/remove
                subtree: true // target childs will be observed | on attributes/characterData changes if they observed on target

            };
            var options = observationOptions || defaultOptions;

            var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
            var observer = new MutationObserver(function anElementIWatchChanged(mutations) {
                doThisOnChange();
            });

            // removing the dom element won't be triggered
            observer.observe(domElement, options);
        };

        return utilService;
    });

and simply you can use it like

$utilsService.observeDomChange($element[0], doSomething());

here is an example of a usage

angular.module(
...
.directive('myCustomDirective', function ($utilsService) {
    return {
        restrict: 'A',
        //...
        controller: function ($scope, $element) {
            //...
            $scope.$on('$viewContentLoaded', function (event, viewName) {
                $utilsService.observeDomChange($element[0], doSomething());
            }
        }
    }
}
Share:
30,298
richbai90
Author by

richbai90

When I'm not developing new solutions for clients using the latest web technologies like ES6, Typescript, React, and Angular, or writing new APIs in Java, Ruby, Python, and PHP, I'm at home cooking masterpiece meals for my family of 5, or in the wilderness taking pictures of the incredible scenery that I'm so lucky to live around.

Updated on July 23, 2020

Comments

  • richbai90
    richbai90 almost 4 years

    This may seem like a silly question but I need to know how to watch the entire DOM of a page and recompile it any time it changes. Essentially this is what AngularJS does by default by use of databindings, but I need this to happen anytime anything in the DOM is changed, not just bindings. The reason why is because I have an app that is built entirely in HTML, Javascript and PHP. It's a single page application, it has a main page and injects PHP into a DIV wrapper within that page.

    I want to make some modifications to it but want to keep my code completely separate from the original code. To do this I need to be able to recompile the DOM anytime a new PHP file with it's own DOM structure is injected. What I have so far does not appear to be working.

    app.directive("watch", function () {
        return function (scope, element, attr) {
            scope.$watch("watch", function(oldValue, newValue) {
                if(newValue) {
                    console.log("there is a new value");
                    console.log("the new value is " + newValue);
                 }
             });
         }
    });
    

    I add the watch attribute the the <body> tag but it doesn't seem to work, when the dom is changed nothing gets logged. Ultimately I'd like to replace the console.log with $compile, but I first need to get the watch to work. Can someone point me to what I'm doing wrong?

  • richbai90
    richbai90 over 10 years
    I appreciate you humoring me, I do want to do this the right way. I could use ng-include portal.php for instance, the problem is I don't know how I'd do this correctly without without rewriting the whole thing. The application I'm dealing with is a cluster and I don't really know of a better way to go.
  • Ben Lesh
    Ben Lesh over 10 years
    I guess if I knew more specifics of why you needed to do this I could give you a less snarky answer. haha.
  • Ian
    Ian over 10 years
    As of right now, in almost all browsers, there's not really a good way to monitor for new DOM elements which have been loaded. - what about MutationObserver? Other than IE, it has pretty good support
  • richbai90
    richbai90 over 10 years
    Well basically they have built hundreds of pages of PHP code that is used for the sole purpose of writing html and javascript. There is no separation of code, it is not restful so there are no requests, basically the buttons fire javascript functions that return php classes that rebuild the dom.
  • Ben Lesh
    Ben Lesh over 10 years
    @Ian doesn't work in Android Mobile, Opera Mini (who cares? lol) or any version of IE less than 11. (that's a big slice of browsers) I guess "almost all" was overstating it.
  • richbai90
    richbai90 over 10 years
    I can see how this would be a terrible idea. I will need to find a different way.
  • wag2639
    wag2639 about 10 years
    This is nice for last resort debugging.