How to watch the DOM for changes AngularJS
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());
}
}
}
}
![richbai90](https://i.stack.imgur.com/zkMcT.jpg?s=256&g=1)
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, 2020Comments
-
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 over 10 yearsI 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 over 10 yearsI guess if I knew more specifics of why you needed to do this I could give you a less snarky answer. haha.
-
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 aboutMutationObserver
? Other than IE, it has pretty good support -
richbai90 over 10 yearsWell 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 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 over 10 yearsI can see how this would be a terrible idea. I will need to find a different way.
-
wag2639 about 10 yearsThis is nice for last resort debugging.