How can I run a directive after the dom has finished rendering?
Solution 1
It depends on how your $('site-header') is constructed.
You can try to use $timeout with 0 delay. Something like:
return function(scope, element, attrs) {
$timeout(function(){
$('.main').height( $('.site-header').height() - $('.site-footer').height() );
});
}
Explanations how it works: one, two.
Don't forget to inject $timeout
in your directive:
.directive('sticky', function($timeout)
Solution 2
Here is how I do it:
app.directive('example', function() {
return function(scope, element, attrs) {
angular.element(document).ready(function() {
//MANIPULATE THE DOM
});
};
});
Solution 3
Probably the author won't need my answer anymore. Still, for sake of completeness i feel other users might find it useful. The best and most simple solution is to use $(window).load()
inside the body of the returned function. (alternatively you can use document.ready
. It really depends if you need all the images or not).
Using $timeout
in my humble opinion is a very weak option and may fail in some cases.
Here is the complete code i'd use:
.directive('directiveExample', function(){
return {
restrict: 'A',
link: function($scope, $elem, attrs){
$(window).load(function() {
//...JS here...
});
}
}
});
Solution 4
there is a ngcontentloaded
event, I think you can use it
.directive('directiveExample', function(){
return {
restrict: 'A',
link: function(scope, elem, attrs){
$$window = $ $window
init = function(){
contentHeight = elem.outerHeight()
//do the things
}
$$window.on('ngcontentloaded',init)
}
}
});
Solution 5
If you can't use $timeout due to external resources and cant use a directive due to a specific issue with timing, use broadcast.
Add $scope.$broadcast("variable_name_here");
after the desired external resource or long running controller/directive has completed.
Then add the below after your external resource has loaded.
$scope.$on("variable_name_here", function(){
// DOM manipulation here
jQuery('selector').height();
}
For example in the promise of a deferred HTTP request.
MyHttpService.then(function(data){
$scope.MyHttpReturnedImage = data.image;
$scope.$broadcast("imageLoaded");
});
$scope.$on("imageLoaded", function(){
jQuery('img').height(80).width(80);
}
Related videos on Youtube
Jannis
Updated on July 12, 2022Comments
-
Jannis almost 2 years
I've got a seemingly simple problem with no apparent (by reading the Angular JS docs) solution.
I have got an Angular JS directive that does some calculations based on other DOM elements' height to define the height of a container in the DOM.
Something similar to this is going on inside the directive:
return function(scope, element, attrs) { $('.main').height( $('.site-header').height() - $('.site-footer').height() ); }
The issue is that when the directive runs,
$('site-header')
cannot be found, returning an empty array instead of the jQuery wrapped DOM element I need.Is there a callback that I can use within my directive that only runs after the DOM has been loaded and I can access other DOM elements via the normal jQuery selector style queries?
-
Tosh over 11 yearsYou could use scope.$on() and scope.$emit() to use custom events. Not sure whether this is the right / recommended approach though.
-
-
Jannis over 11 yearsThanks, I tried getting this to work for ages until I realised I hadn't passed
$timeout
into the directive. Doh. Everything works now, cheers. -
Vladimir Starkov over 11 yearsYes, you need to pass
$timeout
to directive like this:.directive('sticky', function($timeout) { return function (scope, element, attrs, controller) { $timeout(function(){ }); }); };
-
Pencilcheck almost 11 yearsI need to increase the timeout to work, something around 500ms
-
Alberto almost 11 yearsYour linked explanations explain why the timeout trick works in JavaScript, but not in the context of AngularJS. From the official documentation: "[...] 4. The $evalAsync queue is used to schedule work which needs to occur outside of current stack frame, but before the browser's view render. This is usually done with setTimeout(0), but the setTimeout(0) approach suffers from slowness and may cause view flickering since the browser renders the view after each event.[...]" (emphasis mine)
-
keepitreal about 10 yearsI'm facing a similar problem and have found that I need around 300ms to allow the DOM to load before executing my directive. I really don't like plugging in seemingly arbitrary numbers like that. I'm sure DOM loading speeds will vary depending on the user. So how can I be sure 300ms will work for anyone using my app?
-
rryter about 10 yearsCan you elaborate why it "may fail in some cases"? What cases do you mean?
-
Jonathan Cremin almost 10 yearsYou're assuming jQuery is available here.
-
abbood almost 10 yearsnot too happy about this answer.. while it seems to answer the OP's question.. it's very specific to their case and it's relevance to the more general form of the problem (ie running a directive after a dom has loaded) isn't obvious + it's just too hacky.. nothing in it specific about angular at all
-
Nick Devereaux almost 10 years@JonathanCremin jQuery selecting is the issue at hand as per the OP
-
Adam almost 10 yearsThis works for me: See: lorenzmerdian.blogspot.de/2013/03/… I have to set timeout to 400ms.
-
Catfish over 9 yearsCan you explain what the
$ $window
is doing? -
René Stalder over 9 yearsThis is not going to solve the problem, since loaded data does not mean, that they are already rendered in the DOM, even if they are in the proper scope variables bound to DOM elements. There's a timespan between the moment they are loaded in the scope and the rendered output in the dom.
-
z0r over 9 yearsIf I understand correctly,
$timeout(..., 0)
will try to run your code before the page is rendered. If you find you need a non-zero delay then you might need the page to be rendered, so try$window.setTimeout(..., 0)
instead. ... I made a demo in Plunker, but I can't reproduce the problem. Can anyone help? -
Brian Scott over 9 yearsThis works great however if there's a post that builds new items with the directive then the window load will not fire after the initial load and therefore will not function correctly.
-
aaaaaa about 9 years@BrianScott - I used a combination of $(window).load for initial page rendering (my use-case was awaiting embedded font files) and then element.ready to take care of switching views.
-
surrender_to_the_Now about 9 years@ArtemAndreev It works better than all the other solutions. There is no delay. Thanks!
-
mdob almost 9 yearslooks like some coffeescript, maybe it was meant to be $($window) and $window being injected into directive
-
timhc22 over 7 yearsShouldn't even need angular.element because element is already available there:
element.ready(function(){
-
tobius over 7 years@timhc22 element is a reference to the DOMElement that triggered the directive, your recommendation would not result in a DOMElement reference to the pages Document object.
-
Zia Ul Rehman Mughal about 7 yearsWhat if i need to run some initialization code for the directive(say some function which returns configurations etc which depend on page load) closely related problem. If covered in same answer would be great
-
Alexey Sh. almost 5 yearsthat doesn't work properly. I'm getting offsetWidth = 0 through this approach