I recently started to learn Javascript and have prior experience in server side languages such as PHP. The issue I'm having is that I cannot use variables that are defined outside of a function inside the function. I always have to copy the variable into the function in order to get my code to work. I will post an example below.
var first = document.getElementById("first");
var second = document.getElementById("second");
function add () {
alert(Number(first.value) + Number(second.value));
}
Most likely the problem is that your script is being executed when the page is still being loaded, and before the "first" and "second" elements have been created.
Accessing the variables works fine. They just happen to be initialized to "undefined" at the time they are created. Moving the variables inside the function means that they aren't initialized until the function is called, which is after the page has been completely loaded.
Related
This is my test function:
var testFolderId = 'di98kjsdf9...';
function testGetFolder(testFolderId){
folder = DriveApp.getFolderById(testFolderId);
Logger.log("folders: " + folder);
}
It fails when I do this. The error says: INVALID ARGUMENT
However, if I hardcode the id into the 'DriveApp.getFolderById' function, it works.
Any explanation? This makes no sense to me.
When a function is called directly from the script editor/menu/button click/triggers, the following sequence of actions happens:
First, Entire script is loaded and All global statements are executed. This is equivalent to loading a web page with all your script in script tags: <script>...code.gs..</script>
The function you called is called. This is like adding callMyFunction() at the bottom of the already loaded script.
Except in case of triggers, The function you called is run without passing any arguments. Thus all arguments are undefined
Caution ⚠️: If the function is called by a trigger, the first parameter passed is usually the event object, while the rest of the parameters are undefined.
var testFolderId="1dhhddci6";
//When this function is called by IDE, it called without passing any arguments
function testGetFolder(testFolderId){//<=same as calling `testGetFolder()` or `testGetFolder(null)`
//testFolderId is declared in local scope , but is undefined
folder = DriveApp.getFolderById(testFolderId);//<= testFolderId is undefined
Workarounds:
Use default parameters:
//When this function is called by IDE, it called without passing any arguments
function testGetFolder(testFolderId="dhhddci6"){//<=same as calling `testGetFolder()`, but `testFolderId` is passed a value. Also same as calling `testGetFolder("dhhddci6")`
//testFolderId is declared in local scope and is defined(declared and intialized with a value)
folder = DriveApp.getFolderById(testFolderId);//<= testFolderId is "dhhddci6"
If global variables are used, Then the arguments should not be declared.
var testFolderId="1dhhddci6";
//When this function is called by IDE, it called without passing any arguments
function testGetFolder(){//<=same as calling `testGetFolder()`
//testFolderId is NOT declared in local scope, so variable is looked up in global scope(where it is defined)
folder = DriveApp.getFolderById(testFolderId);//<= testFolderId is "dhhddci6"
Further reading:
Scope
Closures
Default parameters
Hoisting
Event objects
Why does this work,
function gettingValue() {
var holder = document.getElementById("testing").value;
document.getElementById("displayer").innerHTML = holder;
}
When the following doesn’t?
var holder = document.getElementById("testing").value;
function gettingValue(holder) {
document.getElementById("displayer").innerHTML = holder;
}
The language is Javascript, I was using Microsoft Edge and Opera browsers.
My guess is that the browser doesn’t perform code unless prompted. So var holder = document.getElementById(“testing”).value gets run in the first example because the function that contains it is called by a button.
When var holder = document.getElementById(“testing”).value is put inside a block of script with nothing ‘prompting’ it using the value holder returns undefined. Replaceing document.getElementById(“testing”) with a string “Blue” doesn’t work either. If a function calls holder the value returned is still undefined. So the browser did not create a varable.
I tried having the js document have;
function gettingValue(holder) {
document.getElementById("displayer").innerHTML = holder;
}
And passing the reference document.getElementById(“testing”).value to holder through the HTML document. It didn’t work, the function wasn’t even called because displayer stayed at Default instead of changing to undefined.
Oh experts of stackoverflow, please summarize how and when a browser reads/performs code.
//I realize this might be a 'discussion' which the tutorial said to avoid, if so I apologize. Tell me if this is so and I will not do it again.
Your guess is right - the variable definitions are run as soon as they are executed by the browser, so
var holder = document.getElementById("testing").value; is going to execute that instruction immediately, regardless whether the DOM structure is ready, since it's in outter-most scope. It all depends on where the code is placed in relation to the application entry point and runtime status.
This can obviously be correct, if that variable is defined in a correct place. Function body will only be executed when the containing function is called. It just 'sits' there, and until it is called, the only concern of the browser is if that code is syntactically correct(conforms to JavaScript specification syntax), i.e. can be parsed.
Your problem doesn't appear to have anything to do with when code is executed.
var holder = document.getElementById("testing").value;
The above defines a variable called holder. It is a global because it is outside of any function.
function gettingValue(holder) {
The function also defines a variable called holder by specifying it as an argument name. This variable is scoped to the function.
When you try to access the variable holder, you access the one in the nearest scope.
That's the argument to the function and not the global.
If you didn't mask it:
function gettingValue() {
Then you would be able to access the global.
Your question is very much about when code executes.
Let's look at each thing you tried. First
function gettingValue() {
var holder = document.getElementById("testing").value;
document.getElementById("displayer").innerHTML = holder;
}
That just tells the browser to create a function that is defined by that code in it. It really does not execute the contents of that function, just defines the function. In order for that function to get executed, you have to have some event or some other piece of code, that is executing, call that function. The call to that function, from some event (like a button press) or from other code, tells it to execute.
Now on your second attempt...
var holder = document.getElementById("testing").value;
function gettingValue(holder) {
document.getElementById("displayer").innerHTML = holder;
}
That first line of code is outside of any function definition and it will probably execute when the page loads. That holder variable DOES get created, and it has global scope, which means any function can access it. But, since you don't event have it inside a document_ready event handler, that "testing" control is probably still undefined (the page is not fully loaded when that statement executes) so you get undefined for the contents of "testing".
For your last example, it is hard to say what is going on without seeing the html that had those "testing" and "displayer" controls.
Bottom line, code gets executed when something calls it. When you load a page, any code that is outside of function declarations, executes and has global scope. Anything defined in a function gets executed when that function is called, either by an event or other code.
The issue with the code in the example is an issue with scope:
Your second example doesn't work as expected because you're referencing two different variables/pointers.
var holder = document.getElementById("testing").value;
function gettingValue(holder) {
document.getElementById("displayer").innerHTML = holder;
}
The following scope tree should explain this better:
GLOBAL SCOPE:
defined `holder` (through `var`)
gettingValue SCOPE
defined `holder` (as parameter)
Because you're defining holder as a parameter for gettingValue, you're no longer able to reference the global scope variable inside of gettingValue because they have the same name.
Specifying a parameter in a function definition is very similar to simply defining that variable within the function itself:
var holder = document.getElementById("testing").value;
function gettingValue(holder) {
document.getElementById("displayer").innerHTML = holder;
}
Is equivalent to:
var holder = document.getElementById("testing").value;
function gettingValue(firstParameter) {
var holder = firstParameter;
document.getElementById("displayer").innerHTML = holder;
}
Instead, you may have success doing one of the following:
function gettingValue(value) {
document.getElementById("displayer").innerHTML = value;
}
var holder = document.getElementById("testing").value;
gettingValue(holder);
OR
function gettingValue() {
var holder = document.getElementById("testing").value;
document.getElementById("displayer").innerHTML = holder;
}
gettingValue();
Notice in both examples above we have to call the function in order to execute it (using gettingValue()).
I hope this helps!
As a side note, Scotch.io has a great article that explains how scope works in JavaScript: https://scotch.io/tutorials/understanding-scope-in-javascript
Alright ; I had two misconceptions that were tripping me up.
I was assuming a local variable (used inside function) would be the same as a global variable if they had the same name. //Now that I realize that was the problem I remember reading about it.
I had thought document.getElementById(“blah”).value would be read from the ’s value every time it was used. This is not true, it is read only once when it is a global varable. As a local varable it is ‘read’ to whenever the function containing it is called.
I was blindsided by;
Global variables in javascript are read before the HTML document puts values into its elements. So that was why var holder = “Blue” returned “Blue” when var holder = document.getElementById(“testing”).value returned undefined. It was an order of operations thing. A value had not been put into the element yet. So my lesson is not to use document.getElement... in global variables.
Thank you all for your time and attention.
I have a JS file where I initialize this variable:
var totalSlideNumber = $(".background").length;
in file scope. I then try to print it to console on the next line:
var totalSlideNumber = $(".background").length;
console.log($(".background").length);
But this always prints 0. When I print the same variable in a function I get 3:
function parallaxScroll(evt) {
console.log($(".background").length);
...
Why are the values different from file scope to function scope?
Why are the values different from file scope to function scope?
It's not a question of "file scope" and "function scope", it's simply that the value is changing between the time you first execute the code and the time you next execute the code.
Specifically, this code:
$(".background")
When that selector queries the DOM the first time, it doesn't find anything. So the number of results is 0. When you execute it again later, it does file matches. So the number of results is 3 in this case.
It sounds like the first time you're executing the code is before the DOM structure has fully loaded. So the elements you're looking for simply don't exist yet. You can wait until the DOM structure is loaded and ready by using a document.ready handler:
$(document).ready(function () {
console.log($(".background").length);
});
Or, shorter:
$(function () {
console.log($(".background").length);
});
I have a problem with QlikView in the browser: I have a listbox and try to access it using an initialize script.
The script is registered by using the InitWorkbench function, using its BodyOnLoadFunctionNames parameter. So far, this works, and the initializer is run at startup.
Inside the initializer I try to do the following:
var doc = Qv.GetCurrentDocument();
var listbox = doc.GetObject('LB01');
Afterwards, when I have a look at listbox.Type, unfortunately it is undefined. If I delay execution of this query, it correctly says LB, hence apparently the query works - but only when it is executed delayed.
So, obviuosly there's a timing problem, and it seems as if the initializer runs too early (or I am doing something wrong).
Can anybody point out what the solution is (or give me a hint on what I am doing wrong)?
Okay, I've found the solution: The internal update function did not run yet, and all the values are only available once this function ran, so you need to provide a callback to the call to GetObject (that gets called after the update function):
var doc = Qv.GetCurrentDocument();
var listbox = doc.GetObject('LB01', function () {
console.log(listbox.Type); // => 'LB'
});
I've got a $.getJSON call in some code that appear to be not updating a global variable, and I'm at a loss to understand why. The JSON data is being loaded OK, but for some reason the global EventOptions array is not being updated in the for {} loop. The capitalised comments refer to the variable. Any ideas? Thanks
function LoadMeasurementTypes() {
// Clear out EventOptions
EventOptions = ["..."];
// Push a couple on to EventOptions - THESE ADD OK
EventOptions.push("Temperature");
EventOptions.push("Pulse rate");
// Call json to get measurementTypes off the table
$.getJSON('./get-measurement-types.php', function (measurementTypeData) {
// Process each json element ([0].BP, [1].ph (Urine) etc.
for (var i = 0; i < measurementTypeData.length; ++i) {
// e is a storage variable to contain the current element
var e = measurementTypeData[i];
// Add the new measurement type
alert(e.measure_type); // OK works - we can see the measure_type
EventOptions.push(e.measure_type); // THESE ARE NOT BEING ADDED
}
} // end anonymous function
) // end get json call
EventOptions.push("Last one"); // THIS ONE IS BEING ADDED
}
Your EventOptions[] is not globally visible. My guess would of been that it should still be visible locally to your $.getJSON call; but because it is now scoped to jquery, its clearly obscured (did you alert(EventOptions); inside your anon function to test?.
To properly scope, just declare it outside of LoadMeasureTypes().
var EventOptions = ["..."];
function LoadMeasureTypes(){...
-update
if this does not work - you could always pull the anonymous function outside of the $.getJSON() and assign it a variable name:
var retreiveTypes = function(){...};
$.getJSON("..path/php", retreiveTypes);
window.EventOptions = ["..."]
Good 'ol "hack" to put stuff in the global context
Got the answer: well kind of. It won't work on iTouch Safari, but is fine on Firefox (Mac). Bosworth I'm figuring it's a browser issue you noted above.
Interestingly, it may be something to do with threads. It appear the out loop runs before the inner anonymous loop has finished (the alerts are not in sequence!). I didn't think javascript used threads this way, but I may be wrong.
I now suspect the whole issue is a timing one - with a new thread as an anonymous function not completing in time.
Thanks guys.