I have a simple thing like this:
function init() {
var $something = 'something';
}
jQuery(document).ready(function($) {
init();
alert($something);
}
I thought this would work, but it doesn't, console says that $something is not defined. What's the issue?
Many thanks!
$something is defined within the scope of the function 'init' so you will only ever be able to access it from within that function as it is. If you wanted to get a value back, you could return it, like so:
function init() {
var $something = 'something';
return $something;
}
jQuery(document).ready(function($) {
var $something = init();
alert($something);
}
Notice that both those variables have the same name (normally a bad idea). They are each defined within their own scope, and thus they are totally different variables.
An alternate pattern might be to wrap the entire thing and use that scope, like so:
(function () {
var $something;
function init() {
$something = 'something';
}
jQuery(document).ready(function($) {
init();
alert($something);
}
})();
That way you have a single variable, but you avoid polluting the global namespace.
Edit:
In response to your comment, the above could be written like:
var newscope = function () {
var $something;
function init() {
$something = 'something';
}
jQuery(document).ready(function($) {
init();
alert($something);
}
}
newscope();
But I have defined the function AND called it at basically the same time without having to give it a name.
This is a scoping issue.
$something is defined within the scope of the init() function, and therefore, it will be disposed of when the init() function completes.
Vars declared with var will be local to the closure in which they are declared. As you've found, this means, therefore, that outside of that closure they are not reachable.
There's many ways round this and each means a different design pattern. Here's one pattern you could use:
({
init: function() {
this.something = 'hello';
jQuery(function() { this.dom_ready(); }.bind(this));
},
dom_ready: function() {
//DOM code here
alert(this.something); //hello
}
}).init();
Here I declare several methods of an object, or namespace. Since they belong to, and are called in the context of, this one object, they communicate between one another with properties rather than variables.
Variables are thus demoted to being useful only within (but never outside of) the closure in which they are declared.
One advantage of this pattern is that you can separate any code that needs to wait for the DOM to be ready from any code that doesn't. This is achieved by having a dedicated dom_ready method.
Related
WARNING!! I AM A NOVICE THROUGH AND THROUGH
Alright, so I know there have been a lot questions about Global variables, and I think that's what I'm looking for, but, not exactly. Lately I've been needing to call upon the same lines of code several times. document.getElementById("example").style or similar to little things like that but I need to continuously repeat.
My question is how do I make it so that I make one variable, outside of the function, to save time writing these lines?
What I've been seeing is to simply write it outside like this var inferno = document.getElementById("inferno"); but this is far from working.
This is my code right now, it's simple because I was just using it as a test, but can anyone help me?
var inferno = document.getElementById("inferno");
function infernoClick () {
inferno.style.backgroundColor="red";
}
You have the right idea. Note, though, that the variable doesn't have to be global. It just has to be where all of the code that wants to use it can use it.
For example, this creates a global:
<script>
var inferno = document.getElementById("inferno");
function infernoClick () {
inferno.style.backgroundColor="red";
}
function somethingElse () {
inferno.style.color="green";
}
</script>
(Note that this needs to be after the markup creating the inferno element.)
The problem with globals is that they can conflict with each other, and in fact the global "namespace" is really, really crowded already.
You can avoid that by wrapping up the code that needs inferno in a scoping function, like this:
<script>
(function() {
var inferno = document.getElementById("inferno");
function infernoClick () {
inferno.style.backgroundColor="red";
}
function somethingElse () {
inferno.style.color="green";
}
})();
</script>
That code creates an anonymous function and then calls it immediately, running the code inside.
Now inferno is "global" to the functions that need it, but isn't actually a global.
Let's take a further example:
<script>
(function() {
var outer = 42;
function doSomethingCool() {
var inner = 67;
document.getElementById("someElement").onclick = function() {
alert("inner = " + inner + ", outer = " + outer);
};
}
// Can't use `inner` here, but can use `outer`
alert("outer = " + outer);
doSomethingCool();
})();
</script>
That code wraps everything in a scoping function, and the outer variable is accessible everywhere within that scoping function. It also has a function, doSomethingCool, which has a variable called inner. inner is only accessible within doSomethingCool. Look at what doSomethingCool does: It hooks up an event handler for when someElement is clicked. It doesn't call the function, it just hooks it up.
The really cool thing is that later, when someone clicks the element, that function has access to that inner variable.
And in fact, that's true for arguments you pass into the function as well. One last example:
<input type="button" id="element1" value="One">
<input type="button" id="element2" value="Two">
<script>
(function() {
function hookItUp(id, msg) {
document.getElementById(id).onclick = function() {
alert(msg);
};
}
hookItUp("element1", "This message is for element1");
hookItUp("element2", "And this one is for element2");
})();
</script>
There, we have this function that accepts a couple of arguments, and we call it twice: Once to hook up click on element1, and again to hook up click on element2.
The really cool thing here is that even though the clicks happen much later, after the calls to hookItUp have long-since returned, the functions created when we called hookItUp still have access to the arguments we passed to it — when we click element1, we get "This message is for element1", and when we click element2, we get "And this one is for element2."
These are called closures. You can read more about them on my blog: Closures are not complicated
That'll work, but only if the declaration appears after the point in the DOM where the element actually appears. Try moving your <script> to the very end of the <body>.
Another thing you can do is use the window "load" event to make sure the whole DOM has been seen before your code runs.
for example
var myGlobalVars = {"inferno":null,"othervar":null}; // globals in their own scope
function clickMe(varName,color) { // generic function
myGlobalVars[varName].style.backgroundColor=color;
}
window.onload=function() {
// initialise after the objects are available
for (var o in myGlobalVars) myGlobalVars[o]=document.getElementById(o);
// execute
clickMe("inferno","red");
}
.
.
T.J. Crowder gave a beautiful answer about scoping; just to add on that you can also use an immediately-invoked function expression to create a module with your UI elements, i.e.
var UI = (function() {
...
return {
inferno: document.getElementById("inferno");
};
})();
...
UI.inferno.style = ...;
I am studying a JavaScript file and saw in it that some of the methods are wrapped inside a jQuery function. Can Anyone help me how to invoke the following method? And may I know what is the advantage or why the method is wrapped in a function? Below is my sample JavaScript code.
JQuery/JavaScript
$(document).ready(function () {
//How to invoke "testMethod" method?
$(function () {
function testMethod() {
alert("this is a test method");
}
});
});
As you've declared it, testMethod() is a local function and is only available inside the function scope in which it is declared. If you want it to be callable outside that scope, you will need to define it differently so that it is available at a broader scope.
One way of doing that is to make it a global function:
$(document).ready(function () {
//How to invoke "testMethod" method?
$(function () {
window.testMethod = function() {
alert("this is a test method");
}
});
});
testMethod(); // available globally now
It could also be attached to a global namespace or it could be defined at a higher scope where it would also solve your problem. Without specifics on your situation, we can't suggest which one would be best, but the main thing you need to do is to change how the function is declared so it is available in the scope in which you want to call it from.
P.S. Why do you have one document ready function nested inside another? That provides no extra functionality and adds unnecessary complexity. Also, there's really no reason to define testMethod() inside your document ready handlers if you want it available globally.
Before anything else:
$(document).ready(function(){...});
//is the same as
$(function(){...}}
As for your question, here's are potential ways to do it:
If that function is some utility function that everyone uses, then have it available to all in some namespace, like in this one called Utility:
//Utility module
(function(ns){
//declaring someFunction in the Utility namespace
//it's available outside the ready handler, but lives in a namespace
ns.someFunction = function(){...}
}(this.Utility = this.Utility || {}));
$(function(){
//here in the ready handler, we use it
Utility.someFunction();
});
If they all live in the ready handler, and want it to be used by all code in the handler, have it declared in the outermost in the handler so all nested scopes see it.
$(function(){
//declare it in the outermost in the ready handler
function someFunction(){...}
//so we can use it even in the deepest nesting
function nestedSomeFunction(){
someFunction();
}
someElement.on('click',function(){
$.get('example.com',function(){
someFunction();
});
});
nestedSomeFunction();
someFunction();
});
Your call needs to be within the $(function.
It's all about scope and you need to break the testMethod out of the $(function.
Can you perhaps further explain your requirement so that we can maybe help a little better?
Into ready event:
$(document).ready(function () {
//How to invoke "testMethod" method?
var testMethod = function () {
alert("this is a test method");
}
// V0.1
testMethod();
// V0.2
$('#some_id').click(testMethod);
});
In other part:
myObj = {testMethod: null};
$(document).ready(function () {
//How to invoke "testMethod" method?
myObj.testMethod = function () {
alert("this is a test method");
}
});
// Something else
if( myObj.testMethod ) myObj.testMethod();
If I want to give a JavaScript variable global scope I can easily do this:
var myVar;
function functionA() {
myVar = something;
}
Is there a similarly simple and clean way -- without creating an object -- to separate the "declaring" and the "defining" of a nested function? Something like:
function functionB; // declared with global scope
function functionA() {
functionB() { // nested, would have local scope if declared here
CODE;
}
}
I should clarify that I'm referring to the scope of the function name itself -- so that if it is in an iframe it can be called from a script in the parent document. (Nothing to do with scope of variables inside nested functions.)
You can create global variables and functions by creating instances on the window object:
function _A()
{
// scoped function
function localFunctionInTheScopeOf_A()
{
}
// global function
window.globalFunctionOutsideTheScopeOf_A = function ()
{
};
}
In your case, though, all you need to do is this:
var myFn; // global scope function declaration
function randomFn()
{
myFn = function () // global scope function definition
{
};
}
Note: It is never a good idea to clog up the global scope. If you can; I'd recommend that you re-think how your code works, and try to encapsulate your code.
Perhaps I'm misunderstanding the question, but it sounds like you want something like this:
var innerFunc;
function outerFunc() {
var foo = "bar";
innerFunc = function() {
alert(foo);
};
}
You cannot globalize variables/functions cross windows/iframes that way. Each window/iframe has it's own global scope and to target variables/functions in another window/iframe, you need explicit accessor code and conform to the same origin policy. Only variables/functions inside the windows/iframes global scope are accessible.
code in top window.
var iframe = document.getElementById('iframeId');
var iframeContext = iframe.contentWindow || iframe;
// this will only work if your iframe has completed loading
iframeContext.yourFunction();
You could also possibly define functions/variables in the top window instead and simply work in one scope by binding the stuff you need from the iframe through a closure. Again, assuming you meet the same origin policy. This will not work cross domain.
code in iframe.
var doc = document;
var context = this;
top.myFunction = function(){
// do stuff with doc and context.
}
It is also important to note, that you need to check if your iframe content and it's scripts are fully loaded. Your top page/window will inadvertidly be done and running before your iframe content is done, ergo variables/functions might not be declared yet.
As for exposing a private function, others have awnsered this, but copy/pasting for completeness.
var fnB;
var fnA = function(){
var msg = "hello nurse!";
fnB = function(){
alert(msg);
}
}
I have the habbit of declaring stand alone functions as variables (function expression) and only use function statements to signify constructors/pseudo-classes. It also avoids a few possible embarrasing mistakes.. In any case, fnB resides in the global scope of the iframe and is available to the top window.
Why exactly you want this beats me, seems it makes matters more complicated to debug or update a few months later.
You can kind of do what you want.
You can create a function that acts like a namespace for properties and methods, and then you could essentially call either...
functionB();
or
functionA.functionB();
There is an article on how to do it here:
http://www.stevefenton.co.uk/Content/Blog/Date/201002/Blog/JavaScript-Name-Spacing/
In response to the update...
Is the iframe on the same domain as the parent site? You can't call JavaScript across the domain boundary, which may explain the problem.
I have made a Web page using jquery and php where all files are used in a modular style. Now I have two JavaScript files which must communicate with each other. One Script generates a variable (id_menu_bar) which contains a number. I want that this variable gets transported to the second JavaScript and is used there.
How do I make that?
Here the Script
menu_bar.js
$(document).ready(function() {
function wrapper_action(id_menu_bar) {
$(".wrapper").animate({height: "0px"});
$("#changer p").click(function() {
$(".wrapper").animate({height: "300px"});
});
}
$("#select_place li").live("click", function() {
var wrapper_id = $(".wrapper").attr("id");
var id_place = this.id;
if (wrapper_id != "place")
{
$("#select_level li").remove();
$("#select_building").load("menu_bar/menu_bar_building.php?placeitem="+id_place, function() {
$("#select_building li").click(function() {
var id_building = this.id;
if (wrapper_id != "building")
{
$("#select_level").load("menu_bar/menu_bar_level.php?buildingitem="+id_building, function() {
$("#select_level li").click(function() {
var id_level = this.id;
wrapper_action(id_level);
});
});
}
else if (wrapper_id == "building")
{wrapper_action(id_building);}
});
});
}
else if (wrapper_id == "place")
{wrapper_action(id_place);}
});
});
if the variable id_menu_bar is in global scope then it can be used by another script on the page.
jQuery's $.data() is also good for storing data against elements and means that you do not need to use a global variable and pollute the global namespace.
EDIT:
In response to your comment, there is a difference in how you declare variables that determines how they are scoped in JavaScript.
Global Variables
Outside of a function declaring a variable like
var myVariable;
or
myVariable;
will make no difference - both variables will have global scope. In fact, the second approach will give a variable global scope, even inside of a function. For example
function firstFunction() {
// Local scope i.e. scoped to firstFunction
var localVariable;
// Global scope i.e. available to all other JavaScript code running
// in the page
globalVariable = "I'm not really hiding";
}
function secondFunction() {
// I can access globalVariable here but only after
// firstFunction has been executed
alert(globalVariable); // alerts I'm not really hiding
}
The difference in this scenario is that the alert will fail and not show the value for globalVariable upon execution of secondFunction() until firstFunction() has been executed, since this is where the variable is declared. Had the variable been declared outside of any function, the alert would have succeeded and shown the value of globalVariable
Using jQuery.data()
Using this command, you can store data in a cache object for an element. I would recommend looking at the source to see how this achieved, but it is pretty neat. Consider
function firstFunction() {
$.data(document,"myVariable","I'm not really hiding");
globalVariable = "I'm not hiding";
}
function secondFunction() {
// alerts "I'm not really hiding" but only if firstFunction is executed before
// secondFunction
alert($.data(document, "myVariable"));
// alerts "I'm not hiding" but only if firstFunction is executed before
// secondFunction
alert(globalVariable);
}
in this scenario, a string value "I'm not really hiding" is stored against the document object using the key string myVariable in firstFunction. This value can then be retrieved from the cache object anywhere else in the script. Attempting to read a value from the cache object without having first set it will yield undefined.
Take a look at this Working Demo for more details.
For reasons not to use Global Variables, check out this article.
Does it have to ve a JavaScript variable?
Can you store the information using the .data() function against a relevant element?
I'm trying to mimic static variables on a JavaScript function, with the following purpose:
$.fn.collapsible = function() {
triggers = $(this).children('.collapse-trigger');
jQuery.each(triggers, function() {
$(this).click(function() {
collapse = $(this).parent().find('.collapse');
})
})
}
How do I save the "collapse" object so it doesn't have to be "found" on each call? I know that with named functions I could do something like "someFunction.myvar = collapse", but how about anonymous functions like this one?
Thanks!
You can save your variable in the function, using either functioName.myVar = value or arguments.callee.myVar = value if you don't have the current function name.
arguments.callee is the current function you are in.
For anonymous function you could use a function that returns a function.
For instance:
var myAnonymousFunction = (function(){
var myFirstStatic = $("#anElement");
var anotherStatic = true;
return function(param1,param2) {
// myFirstStatic is in scope
// anotherStatic also
}
})();
Should work like a charm and you're assured initialisation code for statics is only executed once.
It seems that a better answer to this question is found elsewhere on Stack Overflow.
In short, you can actually give anonymous functions names without polluting the namespace, yet still allow self-referencing.
mything.prototype.mymethod = function myKindOfFakeName() {
myKindOfFakeName.called = true;
}
As long as you're assigning the function to a variable like that, you should be able to access it as $.fn.collapsible, and thus assign variables as $.fn.collapsible.myvar.