jQuery run function after everything has run - javascript

I want to add colspan with jQuery to an element. If I insert this function into console in Chrome then it works fine, but if I put it in document ready and run it in the script then nothing happens. This function should run after all scripts have run if possible. Any tips will help, no access to HTML.
$( document ).ready(function() {
$("#fieldset_Q8aGrid td:eq(4)").attr('colSpan',3)
});

You probably have some asynchronous scripts running which add the content after your code runs. If you do not have access to that other code, then the most pragmatic solution is to try from time to time until it succeeds:
$( document ).ready(function() {
var timer = setInterval(function () {
if ($("#fieldset_Q8aGrid td:eq(4)").attr('colSpan',3).length) {
clearInterval(timer);
}
}, 500); // check every half-second.
});

I am assuming you have some async code in your program that, for example, create dynamic HTML. This is the case if you don't know when all the functions will completely execute.
We can split this problem into two parts.
If you call a function X after a sync code Y, X will happen after Y.
But if you have call a function X after an async code Y, you don't know what will execute first.
To solve 1) you just have to put your code at the end of your script.
To solve 2) you have to use jQuery's deferred and when to explicitly tell your program: execute this when X, Y, Z is done.
Quick explanation (and quite not precise) of what a promise is:
A promise is an object with a then method (thenable). The then method
can receive a function that will be called when the promise is
resolved
From an animation you can use the .promise() method to create a promise that will be resolved when the animation is done.
var animationPromise = $('h1')
.css({'left': '0', 'position' : 'relative'})
.animate({'left': '20px'}, 700, 'linear')
.promise()
If you are making a call to a service, the jQuery's post, get and ajax, return a promise
// save the promise for later use
var asyncPromise = $.get('https://jsonplaceholder.typicode.com/users');
// execute some the function that uses this data
asyncPromise.then(function(users) { /* do something with the data */ })
Maybe, you have some functionality that is neither an AJAX or an animation, in this case you can create your own promise. For example, this promise is resolved from a setTimeout.
var backgroundChange = $.Deferred()
setTimeout(function() {
$('body').css('background', 'lightblue')
backgroundChange.resolve(true)
}, 3000) /* Please note that backgroundChange
doesn't know that it will be resolved after 3 seconds */
Later on, you can use .when(...) to create a promise that is resolved when a set of promises are resolved.
$.when(asyncPromise, backgroundChange, animationPromise)
.then(function() {
$('.container').css('color', 'blue')
//alert('Everything is done!');
})
Here is a working example that solves the two problems https://codepen.io/anon/pen/JWBXLd?editors=1010

You can either rearrange your scripts to change their order, or go with window.load:
$(window).load(function() {
// code
});
But this will fire after literally all your page is loaded. Even images. So it'll be better to go with the first approach.

Related

Issue with waiting for an event to be done then execute another

I'm trying to use jQuery Promises to solve a problem.
I have a UI in which I make an API call and waiting for a response (Which takes a while). I want to wait for the response to see if everything is loaded then do an action. How can I achieve this?
This is my code:
var d1 = new $.Deferred();
if(($('.fa-spin').length + $('.chart-loading').length )==0){
d1.resolve();
} //Logic behind here is, I'm checking for loading icon,
loading spinner and once they are destroyed,
I want to execute something!
$.when(d1).then(function() {
console.log("fetched");
//Run something
});
I want to check if my loading spinner is done, and then fire an event. How can I do this?
When I execute this code, the deferred never gets resolved and fetch is never printed
you'll need to continually check in order to resolve:
var d1 = new $.Deferred();
var testInterval = setInterval(function() {
if(($('.fa-spin').length + $('.chart-loading').length )==0){
clearInterval(testInterval);
d1.resolve();
}
},100);
$.when(d1).then(function() {
console.log("fetched");
//Run something
});
this will check every .1sec if the spinner's gone, and resolve your defer object when the condition is met. although, you could just run your code there instead of as a defer/resolve.
additionally it might be more effective to hook into your actual process rather than the existence of a spinner dom object, consider implementing an existing observer pattern or roll your own: observer pattern

clearInterval() and setInterval() when all animations are complete

I have a function that is repeatedly being called with setInterval creating animations. If there are still animations running I need it to stop calling the function until all the animations are complete. The code I am using is as follows:
EDIT: Added coded where I am removing the animated elements from the DOM, is that the problem?
var serviceLoop = setInterval(serviceQueue, LOOP_POLL_MS); //10 ms
function serviceQueue()
{
//do some animations..
function moveMan(from, to, plane)
{
(function() {
var tmp_from = from;
var tmp_to = to;
var tmp_plane = plane;
var pos = tmp_from.offset();
var temp = tmp_from.clone(true);
temp.css({ "visibility":"visible",
"position":"absolute",
"top":pos.top + "px",
"left":pos.left + "px"});
temp.appendTo("body");
tmp_from.css("visibility", "hidden");
//if (!tmp_plane) tmp_to.css("visibility", "hidden");
temp.animate(to.offset(), moveMan.ANIMATION_TIME, function() {
tmp_to.css("visibility", "visible");
temp.remove();
});
})();
}
if ($(":animated").length > 0)
{
clearInterval(serviceLoop);
$(":animated").promise().done(function() {
serviceLoop = setInterval(serviceQueue, LOOP_POLL_MS);
});
}
}
The problem I am having is after a couple of animations the function passed to done() is never called, and the script stops.
It seems likely that you end up waiting on a promise() that is waiting on some animations, but then you remove some of those objects from the DOM and then their animation never finishes so the promise never resolves.
See this quote from the jQuery doc for .promise():
Note: The returned Promise is linked to a Deferred object stored on
the .data() for an element. Since the.remove() method removes the
element's data as well as the element itself, it will prevent any of
the element's unresolved Promises from resolving. If it is necessary
to remove an element from the DOM before its Promise is resolved, use
.detach() instead and follow with .removeData() after resolution.
One quick hack might be top call .stop(true) on any item that you are removing from the DOM.
In general, this code needs to be rewritten to be safe from that possibility and hopefully to rethink how you approach whatever it is you're trying to do every 10ms because that's generally a bad design. You should use events to trigger changes, not a 10ms polling operation. You have not explained the purpose of this code so it's not clear to me what alternative to suggest.

Prompting a value with setTimeout function

var label = prompt('Label for Vertical Line');
This code returns the value in label which I enter in the prompted field. But I want some time delay to get the prompted value.
I'm using this code:
var label=alertWithoutNotice();
function alertWithoutNotice(){
var lab;
setTimeout(function(){
lab=prompt('Label for Vertical Line');
}, 1200);
return lab;
}
But this doesn't return any value. Please tell me what is wrong?
This is a chronological issue. setTimeout schedules an asynchronous event. That means, until it runs, the rest of your script will continue to execute.
The result is your return lab line is executed before the timeout callback fires. And so on the return, lab is undefined at that point.
As such, you can't return values from asynchronous events, or at least not meaningfully.
Something like jQuery's deferred objects would be your best route to a fix here, but I don't know if jQuery is an option for you.
The problem is that you are returning value before get inside timeout because setTimeout function is asynchronous.
Take a look here to use synchronously: using setTimeout synchronously in JavaScript
Since you are dialing with asynchronous operation the simplest approach here is to use a callback function. For example:
alertWithoutNotice(function(label) {
alert(label)
});
function alertWithoutNotice(callback) {
setTimeout(function() {
callback(prompt('Label for Vertical Line'));
}, 1200);
}
So think of it is that alertWithoutNotice is notified when label is available. When it happens callback is invoked.

Angular Js Callback Function For $timeout

I have that line of codes
...
$timeout(tempFunc, $scope.sync.getDelay());
...
at my temp function I have that line of code at end:
$scope.sync.releasePrivilege();
and everything works well. However when I try:
...
$timeout(tempFunc, $scope.sync.getDelay());
$scope.sync.releasePrivilege();
...
It doesn't. I think that I should write that line as a callback function into timeout. I don't want to change recent functions at my code I can just edit that lines.
Any ideas?
PS: The problem is that:
$scope.sync.releasePrivilege();
is not running after timeout, it immediately runs.
$timeout is a wrapper for setTimeout that gets mocked out during testing. #MarkRajcok is completely right about about why using it as a blocking method doesn't work. Mark's solution would also solve your issue. But if it's not feasible to relocate your code, there is still good news!
$timeout returns a promise (see $q), so you can actually just chain together what you want:
$timeout( tempFunc, $scope.sync.getDelay() ).then( function() {
console.log("I'm called only after the timeout.");
$scope.sync.releasePrivilege();
});
console.log("But I get called immediately.");
And this should work just fine, should you fancy. It still doesn't block. It just ensures that the function within the then call is executed only after the promise is resolved, that is only when the timeout has completed and your method has been called.
Additionally, your function can return data, if needed. So if tempFunc returned a Boolean value that indicated success, you could also access it:
$timeout( tempFunc, $scope.sync.getDelay() ).then( function( result ) {
if ( result ) {
$scope.sync.releasePrivilege();
} else {
// handle the error
}
});
And there was much rejoicing. Yay.
Just as a note: doing a sleep in a browser would be very bad - it'd lock the UI. Asynchronous execution is what makes the web an awesome platform!
Timeout does not provide the equivalent of a "sleep". $timeout puts work (in your case, tempFunc) on the native event queue, and hence tempFunc will be called later (after the browser renders). $scope.sync.releasePrivilege(); will therefore be executed before tempFunc. As you stated, if you want releasePrivilege() to execute after tempFunc(), have tempFunc() call it.

asynchronous javascript prepare function

To speed up my application I want to prepare some data before DOM is ready and then use this data when DOM is ready.
Here's how it might be:
var data = function prepareData(){
...
}();
$(document).ready(function() {
// use data to build page
}
How to prepare the data for later use?
Thanks
You need should use parentheses around the function expression for clarity (and because in a similar situation where you're defining and calling a function but not using the return value, it would be a syntax error without them). Also, when you use a function expression, you want to not give it a name. So:
var data = (function(){
...
})();
or use a function declaration instead:
var data = processData();
function processData() {
...
}
(Why not use a name with a function expression? Because of bugs in various implementations, especially Internet Explorer prior to IE9, which will create two completely unrelated functions.)
However, it's not clear to me what you're trying to achieve. When the browser reaches the script element, it hands off to the JavaScript interpreter and waits for it to finish before continuing building the DOM (because your script might use document.write to add to the HTML token stream). You can use the async or defer attributes to promise the browser you're not going to use document.write, on browsers that support them, but...
Update: Below you've said:
because prepareData is long time function and I assumed that browser can execute this while it's building DOM tree. Unfortunately '$(document).ready' fires before prepareData is finished. The question is how to teach '$(document).ready' to wait for ready data
The only way the ready handler can possibly trigger while processData is running is if processData is using asynchronous ajax (or a couple of edge conditions around alert, confirm, and the like, but I assume you're not doing that). And if it were, you couldn't be returning the result as a return value from the function (though you could return an object that you continued to update as the result of ajax callbacks). Otherwise, it's impossible: JavaScript on browsers is single-threaded, the ready handler will queue waiting for the interpreter to finish its previous task (processData).
If processData isn't doing anything asynchronous, I suspect whatever the symptom is that you're seeing making you think the ready handler is firing during processData has a different cause.
But in the case of asynchronous stuff, three options:
If you're not in control of the ready handlers you want to hold up, you might look at jQuery's holdReady feature. Call $.holdReady(true); to hold up the event, and use $.holdReady(false); to stop holding it up.
It's simple enough to reschedule the ready handler. Here's how I'd do it (note that I've wrapped everything in a scoping function so these things aren't globals):
(function() {
var data = processData();
$(onPageReady);
function processData() {
}
function onPageReady() {
if (!data.ready) {
// Wait for it to be ready
setTimeout(onPageReady, 0); // 0 = As soon as possible, you may want a
// longer delay depending on what `processData`
// is waiting for
return;
}
}
})();
Note that I happily use data in the onPageReady function, because I know that it's there; that function will not run until processData has returned. But I'm assuming processData is returning an object that is slowly being filled in via ajax calls, so I've used a ready flag on the object that will get set when all the data is ready.
If you can change processData, there's a better solution: Have processData trigger the ready handler when it's done. Here's the code for when processData is done with what it needs to do:
$(onPageReady);
That works because if the DOM isn't ready yet, that just schedules the call. If the DOM is already ready, jQuery will call your function immediately. This prevents the messy looping above.

Categories