AngularJS watch DOM change
Solution 1
So here's what I ended up doing:
I discovered you could pass a function to $scope.$watch
. From there, it's pretty straightforward to return the value of the expression you want to watch for changes. It will work exactly like passing a key string for a property on the scope.
link: function ($scope, $el, $attrs) {
$scope.$watch(
function () { return $el[0].childNodes.length; },
function (newValue, oldValue) {
if (newValue !== oldValue) {
// code goes here
}
}
);
}
I am watching childNodes
, not children
, because the childNodes
list holds elements as well as text nodes and comments. This is priceless because Angular uses comment placeholders for directives like ng-repeat
, ng-if
, ng-switch
and ng-include
which perform transclusion and alter the DOM, while children
only holds elements.
Solution 2
If you need to watch for any changes deeper in the element's dom, MutationObserver is the way to go :
.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 directive module for this angular-dom-events
In your case you could
<ul class="unstyled" auto-carousel>
<li class="slide" ng-if="name" dom-on-create="nameCreated()">{{name}}</li>
<li class="slide" ng-if="email" dom-on-destroy="emailDestroyed()">{{email}}</li>
</ul>
Currently only supports dom-on-create
and dom-on-destroy
, but has better performance then the accepted answer because it will only fire once for each dom event, rather than repeatedly check the $watch callback.
Solution 4
Although I don't think it is with angular's recommendations, you could use ng-init which fires upon the initialization of the element:
<ul class="unstyled" auto-carousel>
<li class="slide" ng-if="name" ng-init="recheck()">{{name}}</li>
<li class="slide" ng-if="email" ng-init="recheck()">{{email}}</li>
</ul>
![pilau](https://i.stack.imgur.com/osZQ0.png?s=256&g=1)
pilau
Updated on August 11, 2020Comments
-
pilau almost 4 years
I have one
auto-carousel
directive which iterates through the linked element's children.The children however are not yet loaded in the DOM, because their
ng-if
s expressions have not been parsed yet.How can I make sure the parent directive knows there have been changes to it's DOM tree?
<ul class="unstyled" auto-carousel> <li class="slide" ng-if="name">{{name}}</li> ... <li class="slide" ng-if="email">{{email}}</li> </ul>
I could use
$timeout
but that feels unreliable. I could also useng-show
instead ofng-if
but that does not answer the question and not what I need. -
Elise over 10 yearsThis is nice! I had a situation where I needed to run a function every time an
input
element was added or removed inside a parent. So,$scope.$watch(function () { return $(".parent input").length; }, ...
. This works awesomely, but I am wondering about performance.. -
pilau over 10 yearsWell with that you'll be calling at least one jQuery lookup, (in this case, a very bad one too) on every
$digest
cycle. That's quite bad in terms of performances. You could cache the node and then use jQuery'sfind()
to improve on that -
Simon Thum almost 9 yearsExactly what I was looking for. Thanks!
-
Mario Levrero almost 9 yearsIs a nice one if you can modify your children, not if you are waiting for a third party element to appear
-
IProblemFactory almost 9 yearsThat is interesting, however requires IE 11.
-
commonpike almost 9 yearsTypical .. exactly what I'm looking for, and the docs tell me not to use it .. docs.angularjs.org/api/ng/directive/ngInit
-
Simon Dragsbæk over 8 yearsI did like this instead to listen for the content length on a contenteditable like this: scope.$watch(function () { return element[0].innerHTML.length;}, function (n, o) { /*Do a operation*/ });
-
pilau over 8 years@SimonPertersen that's a clever one for
contenteditable
s! -
Nik Sumeiko about 8 yearsIt's also suggested to compare
newValue
witholdValue
usingangular.equals(newValue, oldValue)
, even comparable values are plain numbers in the use case of this answer. -
Jeff Dunlop over 7 yearsThis is the only thing that worked for me. I needed to run some code after a form was dynamically inserted into the dom.