window.location not evaluating until entire call stack is processed? - javascript

I have a bit of javascript, triggered from an HTML button, that calls a function. This is using Jquery as well, so there are a couple of underlying functions from that that get called in this process, too. In my script I make a couple of changes to window.location in order to communicate to a remote system (which is supposed to fire off different scripts in response to these calls). This window.location definition is not using the HTTP protocol, but FMP, a registered - on my machine anyway - protocol for FileMaker Pro.
Sample code:
function compareJSON() {
dataSession=({ //build object for output });
$.each( dataSession.chapters , function( indexC, value ) {
//compare objects to some others, testing and changing data
});
//Call remote script on other system
window.location= "fmp://blah.dee.com/Blar?script=SaveJSON&$JSONobject=" + JSON.stringify( dataSession );
//Call remote script on other system
window.location="fmp://blah.dee.com/Blar?script=EditJSON";
}
(Keep in mind, since this is using Jquery, that simply pressing the button that calls this compareJSON() function creates a stack of 2 or 3 other functions before running my function. But, even if it were being called directly in some manner, the compare function itself would be on the stack and thus window.location wouldn't get evaluated until the end of that function.)
The problem is that it looks like the Window.Location isn't being finalized/set/sent/whatever until the ENTIRE JS call stack is finished. So, when I click the button that starts these function calls the stack gets a few Jquery functions put on it (e.g. 'handler', 'default', 'each loop'...), then it hits the JS code that I wrote, which in turn adds a few more function calls to the stack; and then there are a few more Jquery functions that added to the stack, etc. But these stacked window.location definitions made in my functions don't actually trigger the remote system until I step all the way through the JS call stack and exit everything. So the window.location is only defined/set to be whatever was last set in the function calls, instead of including all the intervening definitions/sets that occurred in the stack. It's like a variable that gets changed multiple times in the call stack but only gets read once at the end.
Is there a way to force window.location to be evaluated when it is set instead of waiting for whatever the last setting was?
Thanks,
J

You may want to use an iframe:
function callScript(url) {
var ifr = document.createElement('iframe');
ifr.src = url;
// you can even add ifr.onload = function() {doSomething();}; if you want
}
This will allow any number of calls at once.

This might not work, but the timeout idea is to change something like this:
// code code code ...
window.location = newUrl;
// more code ...
into:
// code code code ...
window.location = newUrl;
setTimeout(function() {
// more code ...
}, 1);
That allows the browser an interval in which it can do something before starting the next event loop for the timer handler.

Related

Cannot copy data to the clipboard vanilla js/angular

i am working in an angular project. In a controller, we have a button that is supposed to generate & copy an embed code (similar to youtube) to the clipboard. However, depending on the type of the item, the embed code can only be generated/returned by an ajax call. Have a look at this code:
function copyEmbed(e) {
var embedCode = '';
if (type === "typeA"){
api.items.compile.get({'id': item.selected.id},
function (response) {
embedCode = response.html; //<-- takes time to populate obviously
copyToClipboard();
});
} else {
embedCode = generateEmbedCodeTemplate(); //no ajax here. populates immediately
copyToClipboard();
}
function copyToClipboard() {
clipboard.copyText(); // all seems good but copying will fail as this function is not invoked with a click handler!
}
}
The problem is that because of the ajax call, the code to copy the resulting embed code cannot be in the copyEmbed function scope, as this means the ajax call will not have the time to get the data before copying. If i was able to make everything synchronous, i would be able to get the data and then call the copy command from within the scope of the copyEmbed function, so it would not fail, as the copyEmbed function is bound to a click event. However, in the example, i am handling the ajax call right, but the copyToClipboard function is not invoked with a click handler so the copy command fails. Any ideas, without resulting in hacky setIntervals to check for embedCode contents?
As always, handling async stuff can only happen by actually handling them in an async fashion. So instead of grabbing the embed code at the time of the button click, i am doing so way before, so it is available as a variable or a data-attribute of the element to be clicked. Only adding the answer for people who might come in here with the same brainfart i had when i opened it.

How can I execute a JavaScript function on the first page load?

I am wondering if there is a way to execute a JavaScript function once only on the first ever page load and then not execute on any subsequent reloads.
Is there a way I can go about doing this?
The code below will execute once the onload event fires. The statement checks if the onetime function has NOT been executed before by making use of a flag (hasCodeRunBefore), which is then stored in localStorage.
window.onload = function () {
if (localStorage.getItem("hasCodeRunBefore") === null) {
/** Your code here. **/
localStorage.setItem("hasCodeRunBefore", true);
}
}
Note: If the user clears their browsers' localStorage by any means, then the function will run again because the flag (hasCodeRunBefore) will have been removed.
Good news...
Using localStorage can be tedious because of operators and long winded function names. I created a basic module to simplify this, so the above code would be replaced with:
window.onload = function () {
if (!ls.exists('has_code_run_before')) {
/** Your code here... **/
ls.set.single('has_code_run_before', true);
/** or... you can use a callback. **/
ls.set.single('has_code_run_before', true, function () {
/** Your code here... **/
});
}
};
Update #1
Per #PatrickRoberts comment, you can use the in operator to see if a variable key exists in localStorage, so
if (localStorage.getItem('hasCodeRunBefore') === null)
becomes
if (!('hasCodeRunBefore' in localStorage))
and is less verbose and a lot cleaner.
Secondly, you can set values as you would an object (localStorage.hasCodeRunBefore = true) though it will be stored as a string, not as boolean (or whatever data type your value is).
function toBeExecutedOnFirstLoad(){
// ...
}
if(localStorage.getItem('first') === null){
toBeExecutedOnFirstLoad();
localStorage.setItem('first','nope!');
}
All JavaScript must execute every time a page loads. If the script is on the page, it will execute.
The logic that is executed within the JavaScript included on the page may execute in a different manner depending on the page state, input provided, and any other signals it receives, be it from the server or the client.
If you're using a server side language, you might choose to render a script conditionally, such as the first time a user logs in.
If you need to include the javascript irrespective of context, then you need to listen to other signals.
The simple modern solution is to make use of localStorage. localStorage can be used to store custom string values on custom key values for any given domain.
The code to make use of this would look like:
if (localStorage['...my key here...'] === '...my expected value here...') {
// The page has been visited before
} else {
// The page has not been visited before
// OR
// The user or script has cleared the localStorage value
}
localStorage['...my key here...'] = '...my expected value here...';
That's all well and good if you just need things to work on the client alone. Sometimes you might need the server to know whether or not the page has been visited before.
The (less)simple solution is to use document.cookie:
if (/(?:^|;\s*)...my key here...=...my expected value here...(?:;|$)/.test(document.cookie)) {
// the page has been visited before
} else {
// The page has not been visited before
// OR
// The user or script has cleared the cookie
}
document.cookie = '...my key here...=...my expected value here...';
If you need to defer the execution until the page has finished loading, then simply include the script in an onload callback, either by assigning the event to the window:
window.onload = function () {
// do stuff when the page has loaded
//this will override any other scripts that may have been assigned to .onload
};
or by binding the event handler to the window:
window.addEventListener('load', function () {
// do stuff when the page has loaded
}, false);
It depends on what first page load means to you. It's subjective.
If you want the function to fire once the DOM has been parsed, but only the HTML and no other external resources, bind it to the DOMContentLoaded event.
document.addEventListener('DOMContentLoaded', fn);
Otherwise, if you want to wait for external resources to be loaded and then fire the event, you should bind it to the window object's load event like so:
window.addEventListener('load', fn);
Here are some links from the Mozilla Developer Network that explain the what I just said in more detail:
https://developer.mozilla.org/en-US/docs/Web/Events/load
https://developer.mozilla.org/en-US/docs/Web/Events/DOMContentLoaded
Good luck!
I was facing something similar, the difference in my case was, I wanted to run a code whenever a new instance was being created I needed a certain code to execute, and then later for the rest of the reloads that code should not execute.
For that similar to localStorage solutions above use session storage instead:
fun_RunOnlyOnFirstPageLoad(){}
if(!$window.sessionStorage.getItem(hasRunBefore)){
fun_RunOnlyOnFirstPageLoad();
$window.sessionStorage.setItem(hasRunBefore, true);
}
using the window.sessionStorage instead stores the value only for that session.
this way once the tab is closed (session is over, that value is wiped out) and on every new instantiation, the code is executed.

Ajax .load() won't work when triggered initially

So I have a simple tab system which I handle with the .load function to load the desired content. The problem is that the page itself which contains this tab system is a ajax loaded content. And for some reason the initial call of the tab function to display the initial tab content won't work. But after manually choosing a tab, the load function loads the content properly.
her some code to look at:
The tab handler:
function loadTab(tab) {
$(".tab_a:eq("+otab+")").removeClass("tab_slc");
$('#tab_content').hide();
$('#tab_content').load("include/tab_downloadVersions.html .tab:eq("+tab+")");
$(".tab_a:eq("+tab+")").addClass("tab_slc");
$('#tab_content').fadeIn(function() {});
otab = tab;
}
at the end I call loadTab(tab); and the thing should be initialized. but for some reason the content remains empty. As soon as you manually click on a tab (I have an on click function which calls loadTab(tab) everything starts working)
Because the code by itself works, I think the problem is caused by the other script which handles the page itself. It is also a .load function which loads the page, which loads this tab system.
So do multiple .loads don't like each other? and if so, what can I change?
Thanks in advance ;)
EDIT: I could't post the entire code for some reason, but if you go here you can see the site in action with all the scripts:
n.ethz.ch/student/lukal/paint.net
The tab system is on the download page.
EDIT:-----------------------------------------------------------------------------------------------------------------------------
Big Update
So this is still the same issue but with a slight twist: I did what was recommended in the comments and put my secondary .load() call inside the success call of the first one.
$("#content").load("pages/contact #contentInside", function() {
$("#OtherContent").load("include/info #OtherContentInside");
});
So this works.
But now I had the great idea to make a giant load function. It is a slightly better function than just the plain load, cause it does some fading and stuff. But now I have the same problem, but even more complicated. I created the load function as a "plugin" so the function itself is in a different script file and therefore I can't access the inside of the success function. I solved this problem with a return $.ajax(); and a .done() call. The problem here is that there is some rare case where it just skips the secondary load function. So I am searching for a guaranteed way of controlling the order of the .load calls. Any idea?
The mock-up website is up to date with the new scripts if you wish to take a look. And people were complaining about potential virus spread from my link. For some reason I can't post long code snippets so the site is the best source I got to show everything. If you know a more trustworthy way to share my code please let me know.
We cannot see the rest of your code to tell where the initial call is being invoked from. A set up like the following should work:
$(function() {
var tab = 0;
loadTab( tab );
});
function loadTab(tab) {
//WHAT IS otab???
$(".tab_a:eq("+otab+")").removeClass("tab_slc"); //<<<==== otab
$('#tab_content').hide();
$('#tab_content').load("include/tab_downloadVersions.html .tab:eq("+tab+")");
$(".tab_a:eq("+tab+")").addClass("tab_slc");
$('#tab_content').fadeIn(function() {});
otab = tab;
}
Update
The reason it does not work initial is because otab is not defined the first time the function is called. You have initialized otab at the end of the function but you are using it at the beginning of the function.
UPDATE 2
I have had a chance to look at your code and I just found out what the issues are:
You do not have DOM ready
You are not calling the function on page load.
The following version of your code should work -- try not to use global variable as you're doing with otab. Since you're loading this script at the end of the page (an you are using event delegation) you may get away with DOM ready. Adding .trigger('click') or click() as indicated below should resolve the issue.
//Tab-loader
//Haeri Studios
var tab = 0;
var otab = tab;
var counter = 0;
//click detect
$(document).on('click', '.tab_a', function() {
tab = counter == 0 ? tab : ($(this).attr('id'));
loadTab(tab);
counter++;
return false;
})
.trigger('click'); //<<<<<===== This will call the function when the page loads
//Tab setup
function loadTab(tab) {
//Content Setup
$(".tab_a:eq("+otab+")").removeClass("tab_slc");
$('#tab_content').hide();
$('#tab_content').load("include/tab_downloadVersions.html .tab:eq("+tab+")");
$(".tab_a:eq("+tab+")").addClass("tab_slc");
$('#tab_content').fadeIn(function() {});
otab = tab;
}
//Initialize << WHAT ARE YOUR INTENTIONS HERE .. DO YOU REALLY NEED THIS PIECE?
$.ajax({success: function() {
loadTab(tab);
}});
A partial answer to this problem was to call the loadTab function inside the success call of the page load function, like charlietfl pointed out. But the problem is that there is no need to call the tabloader every time a new page gets called. So I would rather not have a rare call in every page setup function.
I am a bit disappointed by the system on stackoverflow. It seems like if you have not a high reputation level, no one gives a "S" about your questions. Well but at least some input was give, for which I am very thankful.
So by digging deeper into google I found out that the callback can be manually placed in the function where ever you like.
so if we have a function:
foo(lol, function() {
//This after
});
this does stuff after foo() is done. But what if we have another function inside foo() which we also need to wait for:
function foo(lol) {
bar(troll, function() {
//This first
});
}
The bar function is not relevant to the success call of foo. This causes the unpredictable outcome of calls.
The trick is to control when the success function of foo gets called.
If we add a parameter(callback) inside foo and call this "parameter" (callback();) inside the success call of bar, we can make sure the order is guaranteed.
And that's it:
function foo(lol, callback) {
bar(troll, function() {
//This first
callback(); //<-This callback placement defines when it should be triggered
});
}
foo(lol, function() {
//This after
});
We get:
//this first
//this after

How do I set up sequences of functions that I want executed in JavaScript?

I'm working on a JavaScript driven site where I will have a lot of stuff that need's to be executed in a certain order. A lot of the stuff involves animations and AJAX-loading. Some pseudo code could look like this:
Load JSON formated data
Generate HTML-elements using the loaded JSON data and render them inside a div
Make the elements inside the div scrollable using a jQuery UI slider
Randomize a number between 1 and the total number of loaded elements
Make the jQuery UI slider scroll (animate) to the element that represents the randomized number for a duration of 500 milliseconds
Load more JSON formated data
Replace other elements on the page
And so on...
Each step in this is wrapped in a function - one function loads the JSON data, another generates the HTML-elements, a third initializes the jQuery UI slider and so on. Encapsulating the code in functions makes the code easier to read for me, but above all I want to be able to call the functions in different orders depending on what happens on the page and I want to be sure that one function has finished running before the next one is executed.
If there was just regular functions that didn't involve AJAX or jQuery animations I'd just execute the functions I want to execute, one after the other. The problem is that I need to wait for the animations and data retrieving functions to finish before moving on. To aid me both the animation and AJAX methods in jQuery allow me to send along a callback. But here's where I get lost.
What I want it to do is the following:
Load JSON data. If the loading is successful, go on and...
Generate HTML-elements
Make the elements scrollble
Randomize a number between 1 and the total number of loaded elements and pass it to...
A function that makes the jQuery slider slide (animated) to the element. When the animation is finished...
Load more JSON formated data. If the loading is successful, go on and...
Replace other elements on the page
The ideal thing would be if I could set up this sequence/chain of events in one single place, for example inside an event handler. If I want to call the functions in a different order or not call all of them I would just set up a different sequence/chain. An example could be:
Randomize a number between 1 and the total number of loaded elements and pass it to...
A function that makes the jQuery slider slide (animated) to the element. When the animation is finished...
This means that I'd have to be in control over the callbacks in each step.
I hope you understand what I'm looking for. I want to control the entire execution sequence from a single function. This function would be "the conductor of the orchestra" and all the other functions would be the different instrument sections of the orchestra. This conductor functions need's ears so it can hear when the violinist is finished with her solo and can tell the horns to start playing. Excuse me for the corny allegory, but I hopes it makes it easier to understand what I want to do.
Thanks in advance!
/Thomas
Would the jQuery .queue() function help you?
Could you store a sequencer variable that is an array (which you would be able to change) and then call a sequencer at the end of each function?
You could then pass a step code through each function and cross-reference that with the sequencer variable as to what the next step should be.
Pseudo Code:
var func['1'] = function(arg1,arg2,seq) {
//code
sequencer(seq);
}
var func['2'] = function(arg1,arg2,seq) {
//code
sequencer(seq);
}
var func['3'] = function(arg1,arg2,seq) {
//code
sequencer(seq);
}
var func['4'] = function(arg1,arg2,seq) {
//code
sequencer(seq);
}
function sequencer(seq) {
seq = seq + 1;
window.func[seq]
}
I tried executing this code:
var seq = 0;
var func = [];
func[1] = function(seq) {
setTimeout(function() {
console.log("Executing function 1");
sequencer(seq);
}, 2000);
}
func[2] = function(seq) {
console.log("Executing function 2");
sequencer(seq);
}
func[3] = function(seq) {
console.log("Executing function 3");
}
function sequencer(seq) {
seq = seq + 1;
func[seq].call();
}
sequencer(seq);
But the result (in Firebug) is:
Executing function 1
func[seq] is undefined
[Break on this error] func[seq].call();
I think that the problem is caused by context, but I'm not sure. JavaScript is sensitive to the context in which a function is called.
/Thomas
I found that what I was trying to achieve was slightly overkill for my purposes. So I decided to go with a different approach. I can send one or more boolean variables as a parameters to a function and use them to decide whether to execute a second function or not. Here's an example:
$("#justOneStep").click(function() {
loadImage(false);
});
$("#twoStepsPlease").click(function() {
loadImage(true);
});
function loadImage(boolDoMore) {
// Do the stuff that loads an image
...
if(boolDoMore) {
nextFunction();
}
}
function nextFunction() {
// Do some more stuff
...
}
Not very fancy but easy to understand and control and sufficient for my needs at the moment.
/Thomas

Prompt user for confirmation in the middle of a process

I'm looking for a good approach to sometimes pause an action (function/method call) until the user confirms that he wants to do a specific part of that action. I need to do this in an environment that doesn't allow code execution to stop (ActionScript in my case, but an approach for JavaScript should be identical).
To illustrate, this is a mock-up of the action before introducing the user prompt:
<preliminary-phase> // this contains data needed by all the following phases //
<mandatory-phase> // this will be always be executed //
<optional-phase> // this will always execute too, if in this form, but in some cases we need to ask the user if he wants to do it //
<ending-phase> // also mandatory //
What I need is to insert a conditional user prompt, a "Do you want to do this part?", and do <optional-phase> only if the user wants to.
<preliminary-phase>
<mandatory-phase>
if(<user-confirmation-is-needed> and not <user-response-is-positive>){
<do-nothing>
}
else{
<optional-phase>
}
<ending-phase>
When trying to do this in ActionScript/JavaScript I got something like this:
<preliminary-phase>
<mandatory-phase>
if(<user-confirmation-is-needed>){
askForConfirmation(callback = function(){
if(<user-response-is-positive>)
<optional-phase>
<ending-phase>
});
return;
}
<optional-phase>
<ending-phase>
Now both <optional-phase> and <ending-phase> are duplicated. Also because they use objects created in <preliminary-phase> I can't move them to external functions without passing all the data to those functions.
My current solution is that I enclosed each of <optional-phase> and <ending-phase> in some local functions (so that they have access to data in <preliminary-phase>) declared before I ask for confirmation and I call those functions instead of duplicating the code, but it doesn't seem right that the code is no longer in the order it's executed.
What would you guys recommend?
Notes:
1. askForConfirmation is a non-blocking function. This means that the code that follows its call is executed immediately (this is why I have a return; in my approach).
Note: I'm not 100% sure I get your exact circumstances.
The Command Pattern might be suitable here. It's similar to what people are suggesting.
You have an array of commands that get executed in order.
[<preliminary-phase>, <mandatory-phase>, <optional-phase>, <ending-phase>]
Just shift the commands off the array one at a time and call the execute method.
In the optional-phase, check to see if the user confirmation is required, if not then execute an optional code method which dispatches a command complete event, if it is required then show the alert, wait for an event, check the result and either dispatch a command complete event or call the optional method (which will run and then dispatch a command complete).
You can also create a tree of commands so can clearly state the flow of execution without having to mess with the array.
This is how programs like installation wizards work.
It's good in that the order of execution is nice and visible and your code is nicely broken down in to chunks, and the complexity of each step is encapsulated. For example, the optional-phase doesn't know anything about the ending-phase. The optional-phase only knows that the user might need prompted before executing and it handles all of that internally.
http://en.wikipedia.org/wiki/Command_pattern
"Using command objects makes it easier to construct general components that need to delegate, sequence or execute method calls at a time of their choosing..."
"the code is no longer in the order it's executed" seems fine to me actually. It's fine to have code that isn't written in the order it's executed just as long as it's clear. In fact, since your code executes in variable orders I think it's impossible for you to write it in the order it will execute without duplicating code, which is a far greater evil. Pick good function names and your approach would pass my code review.
<preliminary-phase>
<mandatory-phase>
var optional_phase = function() {
<optional-phase>
}
var ending_phase = function() {
<ending-phase>
}
if(<user-confirmation-is-needed>){
askForConfirmation(function(){
if(<user-response-is-positive>)
optional_phase();
ending_phase();
});
return;
}
optional_phase();
ending_phase();
Does this do what you're asking for?
<preliminary-phase>
<mandatory-phase>
if(<user-confirmation-is-needed>){
askForConfirmation(function(){
if(<user-response-is-positive>)
<optional-phase-as-local-function>
<ending-phase-as-local-function>
});
} else {
<optional-phase-as-local-function>
<ending-phase-as-local-function>
}
Not a huge change , but provided this flow works, optional phase is not repeated
<preliminary-phase>
<mandatory-phase>
if(<user-confirmation-is-needed>){
askForConfirmation(function(){
if(<user-response-is-negative>)
{
<ending-phase>
return;
}
});
}
<optional-phase>
<ending-phase>

Categories

Resources