Calling jquery on load finish

javascript

#1

I’m using the following code to launch a jquery dom function:

Discourse.PageTracker.current().on('change', function(url) {window.setTimeout(my_function,300)});

This function makes changes to some divs in a header and below post list in topic view (above suggested topics), but it sees only the header placement, and doesn’t see the second one, which is within the following tags:

 {{#loading-spinner condition=postStream.loadingFilter}}
  {{#if loadedAllPosts}}
     
  {{/if}}
 {{/loading-spinner}}

Is there a way to detect not only the url change, but also loading of posts? I want to call the function after the initial list is finished and ready.


(Ani) #2

This isn’t a perfect fix but here is what works for me…

$(window).load(function(){
	function FixOPBG(){
		if ($("#opacity-background").length > 1) {
			$("#opacity-background").not(':last').remove()
		}
	}

	function UserCard(){$("#user-card").removeClass("hidden").addClass("hidden");}

	function profEdit(){
		if( String(window.location.pathname).match(/(\/users\/)(?:.*)/) ){
			var bghead = $("section.about.has-background"); var bgcss = "";
			bghead.removeClass( "has-background" ).addClass( "no-background" );
			bgcss = bghead.css("background-image");
			$("body.docked").css("background-image", bgcss);
			$("body.docked").addClass("pbackground-fix");
			$("#opacity-background").removeClass("hidden").addClass("hidden");
		}else{
			$("body.docked").removeClass("pbackground-fix");
			$("body.docked").css("background-image", "");
			$("#opacity-background").removeClass("hidden");
			if( $("#main-outlet").length && !$("#opacity-background").length ){
				$('<img/>').attr('src', "/uploads/default/43/17c191e47619fd8c.png").load(function() {
					$(this).remove();
					$( '<div id="opacity-background" style="display:none!important;"></div>' ).insertAfter( "#main-outlet" ).fadeIn(1000, FixOPBG);
				});
			}
		}; FixOPBG();
		$("html").click(UserCard);
	}

	function execmain(){$('body').ready(function(){profEdit(); UserCard();});}; execmain();
	$('a,input[type="button"]').click(function(){ execmain(); }); // Safely rerun the script when a link is pressed.
	Ember.Route.reopen({enter: function(router) { execmain(); }}); // Because Ember likes to make things complicated.
});

You probably want to focus on this part…

$('a,input[type="button"]').click(function(){ execmain(); }); // Safely rerun the script when a link is pressed.
Ember.Route.reopen({enter: function(router) { execmain(); }}); // Because Ember likes to make things complicated.

The main problem with the code above is that it does not run only once. Alternatively you can use…

Ember.View.reopen({
	didInsertElement : function(){
		this._super();
		// Your function here.
	}
});

But I found that method to be unreliable as it runs the function way more times then the method above does.


(Kane York) #3

Try just setTimeout’ing again until the posts exist in the dom, OR the user is no longer viewing a topic?


#4

Thank you. Both replies gave me some ideas to try :slight_smile:


(Ani) #5

A more complete answer to this question would be the following.

There is no built in way to do this however it can be done via a hack. I have revised the code above and ended up with this…

 var vupdated = false;
    
    if(window.jQuery && $){
    	$(window).load(function(){
    		function LoadIt(){
    			if( vupdated === true ){ return }
    			// Your code goes here
    			vupdated = true;
    		}
    
    		Ember.View.reopen({
    			didInsertElement:function(){ // Here is where the magic happens, when the first element is inserted it will set the script to run whenever the body tag is ready.
    				this._super();
    				$('body').ready(LoadIt);
    				vupdated = true;
    			},
    			willDestroyElement:function(){ this._super(); vupdated = false; } // When an element is about to be removed our variable will be set to false thus allowing our function to run again.
    		});
    
    		$('a,input[type="button"]').click(function(){ if( vupdated === false ){ LoadIt(); } }); // Run the script if a link or a button is pressed and a navigation happens.
    		Ember.Route.reopen({enter: function(router) { LoadIt(); }}); // Run the script when a Route is entered. This is ussually enough however this seems to only run when the Route is changed.
    	}
    }

The above works fairly well and in my tests did not run an unnecessary amount of times. However it is still pretty hacky. But that is what works for me, and I hope that it works for whoever maybe reading this as well.


#6

@eviltrout I’m sorry to pull you into this topic but I am honestly completely confused about this subject: is there no clear way to execute a piece of javascript only once per topic load for example?

Using didInsertElement and also something along the lines of Ember.run.scheduleOnce('afterRender', this, this.afterRenderEvent); I am finding the code within afterRenderEvent executes 83 times - and the solution above, while somewhat functional, does indeed seem a bit hacky and also seems to have trouble even running on the initial page load in some cases.

Clearly I am missing something but any direction would be greatly appreciated as I’ve spent endless hours trying to figure this out to no avail.


(Mittineague) #7

In many cases the load event fires only once, after that the rest is AJAX.

I don’t know what you have in mind to do, but perhaps the jQuery ajaxComplete would work for you.
http://api.jquery.com/ajaxcomplete/

AFAICT the event fires every time poll() finishes, so it may still not be what you need.


(cpradio) #8

There is also onPageChange (but that wouldn’t be specific to a topic, it would be anytime someone navigates to a new “page” in Discourse)


#9

Ok! This seems to work - onPageChange does indeed only call once per load!

Ideally there would be a hook or event that could trigger if the topic had completely rendered but I understand of course that could be problematic say people write new posts while one is reading - understandably the use case and lifecycle of ember/Discourse is far more complex than a standard page.