I like to include my javascript in each page that needs it (more general js files go in the document head):
-- item.html --
<div id="item">
<script type="text/javascript" src="item.js"></script>
...
</div>
so in my script file I can now grab the container and use it to find things:
-- item.js --
var container = $('scripts').last().parent();
var f = container.find('form');
however, if the work needs to be done after the page loads:
$().ready(function() {
var f = container.find('form');
});
I have a problem because container scopes across files. consider:
-- index.html --
<div id="item">
<script type="text/javascript" src="item.js"></script>
...
</div>
<div id="link">
<script type="text/javascript" src="link.js"></script>
...
</div>
<div id="part">
<script type="text/javascript" src="part.js"></script>
...
</div>
where the item.js fails because it picks up the last value container was assigned, instead of the one it was assigned at the time that it was declared such that it in essence performs:
$('#part').find('form');
instead of what I want:
$('#item').find('form');
so my question: how do I make the scope of container local to the file where it's declared?
alternatively, how could I do this differently?
Please look into modularizing your code using something like AMD to solve this. This is exactly what AMD was designed to solve.
There are several possible libraries for this. Since you are using jQuery, RequireJS might make the most sense, however there are other solutions as well, such as
CommonJS using Browserify
Webpack
Duo
RequireJS
I don't a environment to test this on, but would this small alteration work?
var container = $('script[src="item.js"]').parent();
You would have to specify the filename for each scope, but that doesn't seem like much of a burden.
JavaScript in the browser is functional scope. Which means variables declared (using the var keyword) belongs to the top most function, or the global scope if there is no function.
So to answer your question, in every file, you can do:
(function() {
var someVariable = 1;
//...
})();
What this does, is first evaluates and creates the anonymous function, and then immediately executes it with no arguments.
Since you are executing a function, any variables that you declare (as long as you use var), will be scoped to the function, and won't be available globally.
Related
I have an MVC 4 project that utilizes javascript bundling.
In my _Layout.cshtml page I have something like this:
#Scripts.Render("~/bundles/scripts/desktop/modernizr",
"~/bundles/scripts/desktop/jquery","~/bundles/scripts/desktop/jqueryui",
"~/bundles/scripts/desktop/jqueryvalidation", "~/bundles/scripts/custom")
There are others, but this is just an example. Within one of my scripts that's called in the custom script I need to reference a global variable that set within the ready function, as shown below:
<script type="text/javascript">
$(function () {
//alert('Page is ready!');
var warning = 10;
var timeout = 20; }); </script>
Problem is, I always seem to get an error within the method that requires the warning and timeout variables. Am I missing something obvious (not to me, though!) on how I should create these variables? Should I var them outside the $Ready, because the js is loading before the page is technically ready?
Where should the global variable go, if everything is already in a render bundle and there are no script blocks?
Thanks!
The warning and timeout variables aren't global. They've only been defined within the function that you provide to the $ function.
I'd generally recommend avoiding global variables where possible, but if you really want to create global variables just use this:
<script type="text/javascript">
var warning = 10;
var timeout = 20;
</script>
Or this:
<script type="text/javascript">
$(function () {
window.warning = 10;
window.timeout = 20;
});
</script>
Thanks for the response.
I don't think adding the variables in the Ready page will work. The functions that require these variables are loaded before the page is 'ready' (per my understanding how this all works), so there are situations on a new page load where the variable will be required but unreferenced.
This is how I'm currently handling it:
I created a new .js file, with the following:
var warning;
var timeout;
Then I created a bundle reference to the file and put it into my #Script.Render stmt in the correct order for scope. Now, I have my global variables, and it's cleanly implemented into my view code. Now, I know that I should be passing around variables vs. having them global, but in this case I don't see a major issue.
I had made flag and made my previous question deleted because of missunderstanding.
I'm working on a classic asp project.
let's say you have so many <script></script> tags in your code.
For instance:
line 10: <script> ..function 1 definition here..</script>
line 200: <script> .. function 2 definition here..</script>
line 5000: <script> ..function 3 definition here..</script>
also at line 6000: I have another tag which is trying to call function1.
is that possible without using *.js file ?
For instance:
line 6000:
<script> function1(); </script>
Those scripts are not defined in <head> tag.
I know its not useful but I need to know is there any way of it or not.
Hope its more clear now!
anything inside the script tags gets run immediately. if you define function a() in your first script element, then it will add a function called a to your global namespace. any javascript you execute later on in other script elements will have access to it.
<script type="text/javascript">
function a() {
alert('hi');
}
</script>
...
<script type="text/javascript">
a();
</script>
Yes, that is possible, assuming function1 is in the global scope (e.g. not in a wrapper function/self-invoking function).
Of course it is possible . You just need to define it in global namespace. Here is the link which should give you an idea and better understanding. It also includes very simple examples.
I am currently coding in this way:
<script type="text/javascript">
var linkObj;
Is this a safe way to store data? My concern is what if a jQuery or other plug-in was to also use the variable linkObj. Also if I declare my variable like this then can it also be seen by other functions in scripts located in other js files that I include?
$(document).ready(function(){
var linkObj;
});
as long as you use the var keyword, any variable defined in that scope won't be accessible by other plugins.
I you declare a variable this way it will be accessible to all scripts running on the page.
If you just want to use it locally, wrap it in a function:
(function() {var linkObj; ... })()
However, this way nothing outside of the function will be able to access it.
If you want to explicitly share certain variables between different scripts, you could also use an object as a namespace:
var myProject = {}
myProject.linkObj = ...
This will minimize how many global names you have to rely on.
Wrap it in a closure:
<script type="text/javascript">
(function() {
var linkObj;
// Rest of your code
})();
</script>
This way no script outside your own will have access to linkObj.
Is this a safe way to store data?
This is not storing data per se, it's only declaring a variable in a script block in what I assume is an HTML page. When you reload the page in the future, it will not hold previous values.
My concern is what if a jQuery or other plug-in was to also use the variable linkObj.
That's a valid concern, like others have pointed out. However, you would expect plugins not to rely on scope outside the plug-in. This shouldn't impact a lot as good plug-in design would likely prevent this from happening.
Also if I declare my variable like this then can it also be seen by other functions in scripts located in other js files that I include?
Yes. As long as their execution is triggered after your script block gets loaded. This normally follows the order in which your script declaration appears in the page. Or regardless of the order they appear on the page if they are executed, for example, after the jQuery DOM 'ready' event.
It's common to hear that is good to avoid 'global namespace pollution', which relates to this concern. To accomplish that you can use a function to contain code, and directly invoke that function in your script block.
(function () {
var a = 1; // the scope is within the function
alert('The variable a is equal to: ' + a);
}) (); // the parenthesis invoke the function immediately
I am referencing JavaScript as follows on an HTML page:
<script type="text/javascript" src="http://code.jquery.com/jquery-1.6.1.min.js"></script>
<script type="text/javascript" src="http://code.jquery.com/mobile/1.0b1/jquery.mobile-1.0b1.min.js"></script>
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false®ion=GB"></script>
<script type="text/javascript" src="js/shared.js"></script>
<script type="text/javascript">
$('document').ready(function() {
// In-page code: call some functions in shared.js
});
</script>
The functions defined in shared.js are not wrapped inside $('document').ready. So:
Is it safe to assume that functions defined in shared.js are available to the "in-page code"?
If I pull out the in-page code into a separate file called local.js (keeping it wrapped in $('document').ready), is it still safe to assume that functions defined in shared.js are available?
Finally, is the fact that I'm not wrapping shared.js inside $('document').ready a problem? I'm finding that if I do wrap it, its functions are no longer available to the in-page code.
The reason for question 3 is that I'm hitting this problem: Uncaught TypeError: Property ... is not a function - after page has loaded
and wondering if it is something to do with how I've organised my code.
UPDATE: Thanks for the answers. It's now clear that using $('document').ready in shared.js would remove those functions from global scope. However, I just want to clarify the original question in point 3.
Can I assume that if I do the following:
inside my in-page code, loaded inside $('document').ready, call a function from shared.js
have the function in shared.js refer to jQuery, Google Maps, or elements on my page
there will be no problems?
In other words, is it safe to assume that the page will have loaded by the time the functions inside shared.js are called, even if I'm not wrapping everything in that file inside $('document').ready?
Is it safe to assume that functions defined in shared.js are available to the "in-page code"?
Yes, As long as those functions are injected into global scope
If I pull out the in-page code into a separate file called local.js (keeping it wrapped in $('document').ready), is it still safe to assume that functions defined in shared.js are available?
Yes, As long as local.js is included after shared.js AND shared.js injects functions into global scope.
Finally, is the fact that I'm not wrapping shared.js inside $('document').ready a problem? I'm finding that if I do wrap it, its functions are no longer available to the in-page code.
Wrapping functions in document.ready takes them outside of global scope.
var foo = 4; // global
$(function() {
var bar = 5; // local
});
foo = bar; // error
You need to inject variables in global scope, this is as easy as doing
$(function() {
/* all your code */
window["SomeGlobalVariable"] = someFunctionIWantGlobal;
});
Yes
Yes
Maybe. If you wrap code in a function you will lose global access to functions defined. For the most part that's a good thing - not polluting the global namespace. You can still access these functions in the global namespace if instead of function foo(){} you do window.foo = function(){};.
This is all irrelevant however, because you either need a dom ready listener or you don't - depending on whether or not you're trying to access the dom in that code. If yes, then wrap it, if not, then don't. As mentioned, either way you can close over your code so as not to pollute the global namespace, or pollute it if you so desire.
It is safe to assume (if the definitions are not hidden inside a closure that cannot be accessed).
//shared.js
function DoThis() {}
function DoThat() {}
It will still work, just embed local.js after shared.js
<script type="text/javascript" src="js/shared.js"></script>
<script type="text/javascript" src="js/local.js"></script>
It did not work, because the functions were wrapped in a closure (the one that will be run on domready), so they are only available inside that closure
$(document).ready(function () { //this is a closure!
function DoSg() {}
//DoSg is only available inside the closure
//cannot be accessed from the outside, it's defined inside
});
Also, it is unnecessary to put function definitions into $(document).ready(). The part that matters is when you call these functions, that should be inside .ready() (well, if it involves DOM stuff or anything that should be done after page load).
Your code organisation is fine as presented. Any functions defined in "shared.js" will be available to the rest of your page, including your $('document').ready(function() block.
However, if you place the functions in shared.js within that block, then you limit the code's scope to the $('document').ready(function() (i.e. nothing else in the page can use it) -- so that's not the way to go if you want to make stuff in "shared.js" available to other parts of your code / application.
Finally, is the fact that I'm not
wrapping shared.js inside
$('document').ready a problem? I'm
finding that if I do wrap it, its
functions are no longer available to
the in-page code.
If you wrap your function inside document.ready those function are not available in the global scope, as function have local scope (I.E inside the function where they are contained)
Here are the problem scripts:
This is from the HTML file:
<script type="text/javascript">
var devices_record = "some string";
</script>
<script type="text/javascript" src="/js/foo.js"></script>
This is from foo.js:
function bar () {
devices_record = "assign new string";
}
The error report by HttpFox is that devices_ record is not defined. What gives? I thought devices_ record will be a globally scoped variable so it should be accessible from anywhere.
Your test case works for me. Here is my complete test:
foo.js:
function bar () {
alert(devices_record);
devices_record = "assign new string";
alert(devices_record);
}
foo.html:
<html>
<head>
<script type="text/javascript">
var devices_record = "some string";
</script>
<script type="text/javascript" src="foo.js"></script>
</head>
<body onload="bar()">
</body>
I get two alerts, the first says "some string", the second "assign new string".
Your problem exists elsewhere in your code.
I believe your given code works. At least executing the code you give above will not cause any errors.
Also, it's better to include your dependencies such as foo.js before using any inline js. This ensures your library is loaded before calling functions provided your library.
Also, assignment operators will not cause 'devices_ record is not defined' errors because you ARE defining it with the assignment operator.
Your error is probably caused by something else.
Declare devices_record in foo.js. Then, reference it from the javascript embedded in the HTML.
You may also consider wrapping your foo.js code in a class, with devices_record as a field/property. This can make for much easier error trapping, among other things.
Tidbit: also, if devices_record declaration were placed before the script inclusion, the script may be able to access it. However, be wary of declaring variables outside of the file that uses them. If you decided to include that script in a page that forgets to declare devices_record, you'll run into errors.