Problem accessing global javascript variables - javascript

My application has something like the following structure
window.object1;
window.object2;
$(document).ready(function() {
window.object1 = new type1object();
});
function type1object() {
//lots of code
this.property = 'property';
window.object2 = new type2object();
}
function type2object() {
//lots of code
this.property = new type3object();
}
function type3object() {
//lots of code
console.log(window.object1);
this.property = window.object1.property;
}
The problem is that whenever I try to access window.object1 from anywhere other than the document ready callback it comes back as undefined, this is even though when I inspect the DOM window.object1 is defined exactly as I expect it to be.
I've tried doing the same as above but using simple global variables instead (i.e. var object1 instead of window.object1) ... Tried declaring initial dummy values for object1 and object2 in various places... but run up against the same problem.
Does anyone know why I can't access my global variables globally?

You have to make sure you are evaluating window.object1 after initiating it.
That is, in your case, only after document.ready finished executing
If you look at this example below you can see that at click both are initialized.
<html>
<body>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js"></script>
<script>
$(document).ready(function() {
window.object1 = new type1object();
window.object2 = new type2object();
//console.log(window.object1);
});
$(document).click(function(){
console.log(window.object1);
console.log(window.object2);
});
function type1object() {
}
function type2object() {
}
</script>

Since you are not setting the value of window.object1 until you are inside the document ready function, you wont be able to access it until it has run.
Nothing in your code shows that you couldn't just remove that document ready call altogether. It is generally reserved for waiting for elements to load in the dom, which it doesn't seem like you are doing. If you somehow do have elements that need to be waited on inside of code that isn't there, just put your script at the bottom of the page, right above the tag. This will do the equivalent of document ready.

writing the code really stripped out made the answer fall out - I was creating something that referenced object1 during the construction of object1.
So I changed it to this, so that the object exists (though with no content) before anything tries to reference it:
window.object1;
window.object2;
$(document).ready(function() {
window.object1 = new type1object();
window.object1.construct();
});
function type1object() {
//lots of code
this.construct = function() {
this.property = 'property';
window.object2 = new type2object();
};
}
function type2object() {
//lots of code
this.property = new type3object();
}
function type3object() {
//lots of code
console.log(window.object1);
this.property = window.object1.property;
}

Related

How do I use a function as a variable in JavaScript?

I want to be able to put the code in one place and call it from several different events.
Currently I have a selector and an event:
$("input[type='checkbox']").on('click', function () {
// code works here //
});
I use the same code elsewhere in the file, however using a different selector.
$(".product_table").on('change', '.edit_quantity', function () {
// code works here //
});
I have tried following the advice given elsewhere on StackOverflow, to simply give my function a name and then call the named function but that is not working for me. The code simply does not run.
$(".product_table").on('change', '.edit_quantity', function () {
calculateTotals() {
// code does not work //
}
});
So, I tried putting the code into it's own function separate from the event and call it inside the event, and that is not working for me as well.
calculateTotals() {
// code does not work //
}
So what am I doing wrong ?
You could pass your function as a variable.
You want to add listeners for events after the DOM has loaded, JQuery helps with $(document).ready(fn); (ref).
To fix your code:
$(document).ready(function() {
$("input[type='checkbox']").on('click', calculateTotalsEvent)
$(".product_table").on('change', '.edit_quantity', calculateTotalsEvent)
});
function calculateTotalsEvent(evt) {
//do something
alert('fired');
}
Update:
Vince asked:
This worked for me - thank you, however one question: you say, "pass your function as a variable" ... I don't see where you are doing this. Can you explain ? tks. – Vince
Response:
In JavaScript you can assign functions to variables.
You probably do this all the time when doing:
function hello() {
//
}
You define window.hello.
You are adding to Global Namespace.
JavaScript window object
This generally leads to ambiguous JavaScript architecture/spaghetti code.
I organise with a Namespace Structure.
A small example of this would be:
app.js
var app = {
controllers: {}
};
You are defining window.app (just a json object) with a key of controllers with a value of an object.
something-ctlr.js
app.controllers.somethingCtlr.eventName = function(evt) {
//evt.preventDefault?
//check origin of evt? switch? throw if no evt? test using instanceof?
alert('hi');
}
You are defining a new key on the previously defined app.controllers.somethingCtlrcalled eventName.
You can invoke the function with ();.
app.controllers.somethingCtlr.eventName();
This will go to the key in the object, and then invoke it.
You can pass the function as a variable like so.
anotherFunction(app.controllers.somethingCtlr.eventName);
You can then invoke it in the function like so
function anotherFunction(someFn) { someFn();}
The javascript files would be structured like so:
+-html
+-stylesheets
+-javascript-+
+-app-+
+-app.js
+-controllers-+
+-something-ctlr.js
Invoke via chrome developer tools with:
app.controllers.somethingCtlr.eventName();
You can pass it as a variable like so:
$(document).ready(function() {
$('button').click(app.controllers.somethingCtlr.eventName);
});
JQuery (ref).
I hope this helps,
Rhys
It looks like you were on the right track but had some incorrect syntax. No need for { } when calling a function. This code should behave properly once you add code inside of the calculateTotals function.
$(".product_table").on('change', '.edit_quantity', function () {
calculateTotals();
});
$("input[type='checkbox']").on('click',function() {
calculateTotals();
});
function calculateTotals() {
//your code...
}
You could just condense it all into a single function. The onchange event works for both the check box and the text input (no need for a click handler). And jQuery allows you to add multiple selectors.
$('input[type=checkbox], .product_table .edit_quantity').on('change', function() {
console.log('do some calculation...');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="product_table">
<input type="checkbox">
<input class="edit_quantity">
</div>

MVC - Construct javascript object in my index that will be available in my script file

I want to initialize in my index javascript object with urls as properties, I need to initialize it on my view because of the #Url.Action that available in my view. so it will look like this:
Index.cshtml:
window.onload = function () {
myUrls=new Object();
myUrls.url1='#Url.Action("MyAction1","MyControllerName")';
myUrls.url2='#Url.Action("MyAction2","MyControllerName")';
myUrls.url3='#Url.Action("MyAction3","MyControllerName")';
myUrls.url4='#Url.Action("MyAction4","MyControllerName")';
}
Now I have script in my Script folder and I want to access these urls in my script.
How can I achieve this? Can I initialize this object somehow in my script instead of my view?
Because you've done this inside a function, the myUrls variable is scoped to that function. Once the function ends, myUrls goes out of scope and is no longer available. To make it stay around, you have to make it global by either taking it out of the window.onload (which doesn't make any sense anyways for a static variable declaration), or simply declare the variable first in the global namespace.
<script>
var myUrls;
window.onload = function () { ... }
</script>
But again, like I said, you don't need the window.onload because you don't have to wait for the DOM to be ready to declare a variable. So just do:
<script>
var myUrls = {
url1: ...,
url2: ...,
...
}
</script>
You don't need to create a new object explicitly, just use the object notation { ... }.
Finally, since you're adding this to the global namespace, I seriously recommend that you create your own namespace:
<script>
var MyAwesomeAndUniqueNamespace = MyAwesomeAndUniqueNamespace || {};
MyAwesomeAndUniqueNamespace.myUrls = {
...
}
</script>

Why does "this.myFunction" not work when calling a function inside an object?

Here are two samples of code. The first one does not work and the second one does, though I'm completely at a loss as to why. Can someone explain this?
[I'm writing a simple game using a bit of jQuery to be played in a webkit browser (packaged with Titanium later).]
In the first example, Firebug tells me that "this.checkCloud" is not a function.
function Cloud(){
this.checkCloud = function(){
alert('test');
}
$("#"+this.cloudName).click(function(){
this.checkCloud();
});
}
...but then this works:
function Cloud(){
this.checkCloud = function(){
alert('test');
}
var _this = this;
$("#"+this.cloudName).click(function(){
_this.checkCloud();
});
}
This one works perfect.
Why does the first one not work? Is it because "this.checkCloud" is inside of the anonymous function?
in this example:
$("#"+this.cloudName).click(function(){
this.checkCloud();
});
this referrers to the element selected(jquery object).
what you can do is use private functions
var checkCloud = function(){
alert('test');
}
this way you can simply call it inside your anonymous function
$("#"+this.cloudName).click(function(){
checkCloud();
});
That is because the meaning of this can potentially change each time you create a new scope via a function. The meaning of this depends on how the function is invoked (and the rules can be insanely complicated). As you discovered, the easy solution is to create a second variable to which you save this in the scope where this has the expected/desired value, and then reuse the variable rather than this to refer to the same object in new function scopes where this could be different.
Try this:
function Cloud(){
this.checkCloud = function(){
alert('test');
}
var func = this.checkCloud;
$("#" + this.cloudName).click(function(){
func();
});
}
When you assign an even listener to an element, jQuery makes sure that this will refer to the element. But when you create the _this variable, you're creating a closure that jQuery couldn't mess with, even if it wanted to.

Help with Javascript scope

This example is a simplified version of my code. I'm still trying to grasp the new way of writing javascript (as opposed to the way 10 years ago) so thanks for your patience. I need globalVal's value to be accessible and I'm having trouble. The value is obtained from a function that is called as an argument from another method. The example is probably easier to see. Just need to be able to have access to globalvar from everywhere in the DOM. Is this possible? Thanks
<html>
<head>
<script type="text/javascript">
var globalvar;
function initialize() {
var someVariable = 5;
doSomething(someVariable, getTheVar);
}
function doSomething(someVariable, expectGlobalVar) {
//alert(someVariable);
alert(expectGlobalVar);
}
function getTheVar() {
globalVar = "test";
return globalVar;
}
</script>
<title></title>
</head>
<body onload="initialize()">
This is a test
</body>
</html>
You're mostly fine, you can directly access globalVar from any script running anywhere in the page if you declare it the way you have.
Specifically: Using var x; at page-level scope (that is, outside of any function) declares a property on the window object (it has a special feature in that it can't be deleted, but that's not important here).
var foo = 2;
window.foo = 2; // Basically the same other than the delete thing we're not worrying about here
And so:
var foo = 2;
alert(foo); // alerts "2"
alert(window.foo); // also alerts "2"
window.bar = 4;
alert(window.bar); // alerts "4"
alert(bar); // also alerts "4"
Naturally this is only true at the top level, outside of any functions. Inside functions, you're declaring something local to the function. (In essence; it's actually a lot more interesting than that.)
But since you've asked about scope, it's worth nothing that all of the other things you've defined (initialize, getTheVar, doSomething) are also globals. In general, you want to avoid putting anything in the global namespace that you can avoid putting there.
For that reason, I advocate always using a "scoping function":
(function() {
// your code here
})();
...and explicitly exporting exactly and only the things you really need to be global (by assigning them to properties on window).
In your case, you've said you need globalVar and you've also used initialize (although there are other ways to do what you're doing in initialize), so you could do this:
(function() {
var globalvar;
// Exports
window.globalVar = globalVar;
window.initialize = initialize;
// Implementation
function initialize() {
var someVariable = 5;
doSomething(someVariable, getTheVar);
}
function doSomething(someVariable, expectGlobalVar) {
//alert(someVariable);
alert(expectGlobalVar);
}
function getTheVar() {
globalVar = "test";
return globalVar;
}
})();
But you can take it further. Since you're not calling initialize until the load event of the body element, you could avoid publishing initialize. Just put your script tag at the end of the document, just before the closing </body> tag (as the YUI folks recommend), and do your initialization there:
<html>
<head>
<title>...</title>
</head>
<body>This is a test
<script type='text/javascript'>
(function() {
var globalvar;
// Initialization
initialize();
// Exports
window.globalVar = globalVar;
// Implementation
function initialize() {
var someVariable = 5;
doSomething(someVariable, getTheVar);
}
function doSomething(someVariable, expectGlobalVar) {
//alert(someVariable);
alert(expectGlobalVar);
}
function getTheVar() {
globalVar = "test";
return globalVar;
}
})();
</script>
</body>
</html>
The DOM is fully loaded and ready to go at that point.
But we can go even further if we want: We can have nothing in the global namespace if we like. If you hook up all of your handlers within your initialize function rather than using onload, onclick, and similar attributes, there's no need for globalVar to be global except to your code. (You hook up handlers after the fact by using attachEvent [on IE], addEventListener [on standards-based browsers], or better yet using a library like jQuery, Closure, Prototype, YUI, or any of several others.)
You should call function getTheVar instead of passing it:
function initialize() {
var someVariable = 5;
doSomething(someVariable, getTheVar());
}
You're doing it right.
Any variable which is declared in global scope, just like you have in the example, will be available from every scope in the window.
(BTW, declaring a global var is [almost] equivalent to window.myVar = someValue;)
The problem in your example is that you are not actually calling getTheVar on the fourth line, but rather just passing the function itself. You probably want this:
doSomething(someVariable, getTheVar());

Javascript namespace init when called from jquery.ready()

Is the following method wrong way of declaring namespace in Javascript? It is from the book I'm reading and doesn't seem to work in my code.
<script type="text/javascript">
var mynamespace = {};
if(Drupal.jsEnabled){
$(document).ready(mynamespace.init);
}
mynamespace.init = function() {
$("#mybutton").bind("click",function(){
alert('hello');
});
}
</script>
It looks like your code is dependent on jQuery. Make sure that is loaded before you run this script. Also, define your function before it gets called. Try this:
var mynamespace = {};
mynamespace.init = function() {
$("#mybutton").bind("click",function(){
alert('hello');
});
}
if(Drupal.jsEnabled){
$(document).ready(mynamespace.init);
}
What seems to be going wrong here is that the mynamespace.init function isn't defined at the time you are hooking it up to $(document).ready.
This should work as expected:
<script type="text/javascript">
var mynamespace = {};
mynamespace.init = function() {
$("#mybutton").bind("click",function(){
alert('hello');
});
}
if(Drupal.jsEnabled){
$(document).ready(mynamespace.init);
}
</script>
You may also consider forming it like this, as it is easier to understand (at least to me anyhow)
<script type="text/javascript">
var mynamespace = {
init : function() {
$("#mybutton").bind("click",function(){
alert('hello');
})
};
if(Drupal.jsEnabled){
$(document).ready(mynamespace.init);
}
</script>
Be careful using $(document).ready(mynamespace.init);. When executed in this way, this is no longer a reference to mynamespace, normally it would be equal to window, but jQuery do some call magic in the background to set it equal to document. It won't hurt you in this instance, but be wary of it in the future.
$(document).ready(function () {
mynamespace.init();
});
Is how I would do it.
In this case, the reason your code isn't working is that the init method of mynamespace isn't defined at the document $(document).ready(mynamespace.init) is called.
What strikes me as odd, at least working mostly in C#, is that a namespace should not directly contain code logic. In C# it can't, that is what classes are for. So for me, having an init function on a namespace is a contradiction.
Yes, you can use object as a namespace. Another way to form a namespace is to use closures:
(function(inner_variable_1, inner_variable_2) {
// define whatever here, they won't pollute namespace outside this closure
})(outer_variable_1, outer_variable_2);
Popular example is jQuery's noconflict-mode, which enables you to use $ variable in your jQuery code without making $ a global variable, thus leaving the global $ for another use.

Categories