I am trying to create namespaces in JavaScript as in the following script:
var hlAdmin = hlAdmin || {};
hlAdmin.editCompany = function (src) {
// function script
}
Then I call the function in HTML:
onclick="hlAdmin.editCompany(123)"
I get a reference error: Cannot find "editCompany".
Anyone know why?
Based on your comments I assume the following:
The equivalent script (and scoping is like):
<html><head>
</script>
var hlAdmin = hlAdmin || {};
hlAdmin.editCompany = function (src) {
// error in this script
}
</script>
</head></body>
<button onclick="hlAdmin.editCompany(123)">Caption</button>
</body></html>
In this example hlAdmin is indeed in the global scope (the root-scope of the host, called window in browsers).
If (in this example) you get reference error: Cannot find "editCompany", then one should look at other error-messages in your (browser's) error-log, because when there is a fatal error in the function for hlAdmin.editCompany, then that function will not be created (hence .editCompany becomes a property that points to undefined instead of a method that points to the function OR .editCompany doesn't even exist (depending on engine/error)).
To investigate if you indeed have a scoping-problem you could test this by: window['hlAdmin'] || (window['hlAdmin']={}); (or some equivalent variant). If that made the code work, then it seems you have some scoping-problem.
Hope these steps help someone in the future.
It's generally considered bad form to mix inline javascript and non-inline. The preferred way to do this would be to keep all the javascript in one place using an event handler:
window.hlAdmin = window.hlAdmin || {};
window.hlAdmin.editCompany = function (src) {
// function script
}
document.getElementById('yourElementId').onclick = function() {
hlAdmin.editCompany(123);
};
To more specifically address the issue: One thing that could cause this issue is if the hlAdmin object is not ending up in the global scope. You stated that this declaration is "at the top of the JavaScript file", but if it's in any kind of function (such as a function set to window.onload, or the jQuery $(function() { ... });) it would not end up in the global scope when declared as a var. A variable declared with var will only end up globally scoped if it's in the root scope, outside of any kind of function. If rather than using var hlAdmin you instead use window.hlAdmin, this will make sure that even if you're inside a document ready function or something similar, you're creating your hlAdmin in the global context, which will fix the problem if it is in fact an issue of scope.
I found the problem.
The browsers (at least Aurora and Chrome) are dropping the namespace in the onclick attribute. When you look at the browser html the namespace has just disappeared from the markup.
Related
I am creating a wrapper for some arbitrary code (let's call it managed code). The managed code may include some functions that are defined in the window scope and are expected by other scripts on the page (horrible, 1997, practices, I know, but such is what I have to deal with), as global functions.
The purpose of the wrapper is to delay executing the wrapped code until jQuery is loaded. It looks like this:
(function () {
var once = true,
check = setInterval(function () {
if (window.$ && once) {
once = false; // setInterval can stack up if the UI freezes. Ensure this only gets called once.
executeBundle();
clearInterval(check);
console.log('Jquery loaded');
}
}, 100);
})()
// Wrapper proper
function executeBundle() {
// oodles of code of any origin
}
Now that the managed code is wrapped inside the executeBundle function, all functions/variables declared within it will be scoped to that function. This isn't a problem for the managed code itself, but for other scripts that load separately that may rely on global functions it provides.
I'd like to know if anyone knows a strategy like eval, but without the security issues, that may allow me to preserve the window scope for the running of the managed code. The constraint is that I can't modify the managed code at all--just the wrapper.
Based on T.J. Crowder's phenomenal answer, I realized that I could add the managed code to a <script> element and add that to the <head> like this:
var codeBundle = // Code in one long string
function evaluateBundle() {
var script = $('<script type="text/javascript"/>')
script.html(codeBundle);
$('head').append(script);
}
And let the parser evaluate the code.
I'd like to know if anyone knows a strategy like eval, but without the security issues
If you're evaling code of your own that you would run by having it in a script tag anyway, there are no security issues. You're running code either way.
You can't do this if the code you're wrapping will appear directly within evaluateBundle and it has declarations (vars and function declarations) that were supposed to be at global scope. Handling those would require modifying the wrapped code.
You can do this if you load that code separately, though, and then do a global eval on it. For instance, put it in a script block with a non-JavaScript type so the browser doesn't execute it:
<script type="x-code-to-wrap"></script>
...and then:
function evaluateBundle() {
var code = document.querySelector('script[type="x-code-to-wrap"]').textContent;
(0, eval)(code);
}
(The (0, eval)(code) bit is the global eval, more on MDN).
You may have to adjust the textContent part of that for cross-browser compatibility. This question's answers suggest using jQuery's html function:
function evaluateBundle() {
(0, eval)($('script[type="x-code-to-wrap"]').html());
}
Live example on JSBin
Following http://processingjs.org/articles/PomaxGuide.html for using Processing sketches on webpages, one of my functions utilizes this perfectly:
function drawSomething() {
// some calculations
var pjs = Processing.getInstanceById('canvasID');
var number = 5 // placeholder result of calculations
pjs.drawText(number);
}
Yet with another function, drawSomethingElse, the same pjs variable definition logs:
TypeError: pjs is undefined
All the code is wrapped in docReady, and drawSomething(); is called when the page loads:
$(document).ready(function(){
// do lots of stuff
drawSomethingElse();
}
Scope in javascript works like this. If you declare a var or function inside another function it's only visible inside this function
function outerScope(){
var outerVariable = "is defined in outer scope";
function innerScope(){
var innerVariable = "is defined in inner scope";
console.log(outervariable); //innerScope can see outerVariable (through a closure)
}
console.log(innerVariable) //=undefined outerScope can't see innerVariable
console.log(outerVariable) //outerScope can still see outerVariable
}
console.log(outerScope) //= function, global scope can see outerScope
console.log(outerVariable) //=undefined but global scope can't see inside outerScope
console.log(innerScope) //= undefined, therefore it can't see innerScope
console.log(innerVariable) //=undefined and of course not into inner scope
This is true for all functions, including jQuery functions, they are no exception to this rule. So that's why you have to define a var in the scope you want the scope "layer" you want to use it. And to not pollute the global scope you wrap things into these anonymous functions, just to add a scope "layer"
This model always applies, no matter how many layers you add. You will always be able to understand the behavior. (btw always check all the things with console.log you are unsure about, it helps to track down bugs. the more precise you can answer what is wrong with your solution the better you know how to fix it)
Adapting what you know about scopes and since you didn't define Processing in the current scope you know it therefore must be in global scope, means you can open your browser console and just console.log(Processing) and maybe call the method Processing.getInstanceById() yourself in the console a few times. Maybe it's not the canvas id, maybe it's the name of your sketch that defined the name of the instance. Try it out.
Since you now know that your .pde sketch isn't loaded by the time you want to get the instance via javascript, you have a few options. The easiest would be to make the sketch part of the document, so the $(document).ready() only fires and execute your javascript when both, processing and the sketch are loaded.
Usually processing checks the custom data-processing-sources attribute on the canvas and sends a asynchronous request for the files (your sketch). But since it's asynchronous it's not part of your document loading, so the document is ready but your sketch isn't.
If you instead put the sketch code in a script tag inside the document the document won't be ready until it's loaded. You also need to set the mime type or the browser will think this is javascript and throw an error. It doesn't change anything else, it's just another way of setting up your Processing Sketch.
<script type="text/processing" data-processing-target="canvasID">
//your sketch code
</script>
<canvas id="canvasID"></canvas>
And for you to still be able to load your sketch externally here comes the slightly more confusing 3rd way to set up your sketch. Remove the whole script tag and your sketch.
Skip the data-processing-target and data-processing-sources attributes, and instead of pjs = Processing.getInstanceById write
$(document).ready(function(){
var xhr = new XMLHttpRequest();
xhr.open("GET", "yourSketch.pde");
xhr.onload = function(){
var code = xhr.response;
var canvas = document.getElementById("canvasID")
pjs = new Processing(canvas,code);
//rest of your code
}
xhr.send();
});
Note: This technique won't work if you view your website locally from the file:// protocol
pjs scope is drawSomething function for using it in different function change your code like this
(function() {
var pjs = Processing.getInstanceById('canvasID');
function drawSomething() {
var number = 5 // placeholder result of calculations
pjs.drawText(number);
}
function someotherfunction() {
drawSomething();
}
}());
now you can use pjs anywhere in this anon function
The Google Analytics tracking code looks like this:
(function() {
code
})();
What's the technique they are using with those brackets - (stuff)() - called? What does it do?
I put the Google Analytics code before the closing head tag on my page and then put an if statement around it like this (I include a Jquery cookie plugin further up):
<script type="application/javascript">
if ($.cookie('allowcookies') == 'yes') {
analytics code
}
</script>
It didn't run until I used the same technique around my code:
(function() {if ($.cookie('allowcookies') == 'yes') {
analytics code
}
})();
Why did it not run before I did that? Why did it run after?
(function() {
/* code */
}());
It's commonly known as «self executed anonymous function (¹)» (o «immediate function invocation») and its main use is to avoid the creation of variables into the global (or in the outer) scope.
It's also used as shortcut when you want to create a function to execute just once, without the need to first define the function with its own identifier and then soon make the function call.
It may be eventually used inside a scope and then it may create a closure if the outer context (or other references) are binded through parameters passing, e.g.
/* outer scope */
(function(outerscope) {
element.onsomeevent = function() {
/* do something with outerscope */
};
}(this));
Another practical use I make with this expression is when I need to create a function to be soon executed inside a constructor function when it is called with new keyword (instead of an explicit call to some init method).
(¹) — as stated on book "Mantainable Javascript" by Nicholas Zakas (O'Reilly, ISBN 978-1-449-32768-2) page 44, the suggested expression is (function() {}()), with nested parens (even if (function() {})() will work anyway)
[...]To make it obvious that immediate function invocation is taking place, put paretheses around the function[...]
See also Immediate function invocation syntax
The "function(){code}" part only creates a function, the () at the end call the created function.
You could rewrite
(function() {
code
})();
As
var x = function() {code};
x();
It's just a select calling function. The () at the end causes it to be called automatically.
It's used like this to isolate local variables that are relevant only to your code from the global scope.
For example:
(function() {
var x = 5;
window.y = 6;
})();
x is available only in the scope of the function, y is globally available through the window.
As to it not running, I'd hazard that's down to the conditional you supplied.
I'm refactoring about 600 lines of javascript into the module pattern. Here is a start from previous post:
I undertand the concept of anonymous methods...and sefl-executing....but not the scoping concepts...i.e. what global and window do.
window.onload=initialize_page;
(function (global) {
global['test'] = 'test';
function initialize_page()
{
/* fill here */
}
})(window);
Can I put window.onload=initialize_page into my module pattern? Or does it need to be put outside of it? Can someone explain how the access works?
EDIT 1: per Answer
(function () {
addEventListener('load', initialize_page);
function initialize_page()
{
alert ("hi");
}
})();
It is a global. Don't touch it like that, you'll overwrite any other code that tries to assign load handlers.
Use addEventListener (or attachEvent for old IE) instead. There are plenty of libraries that abstract the functionality.
I'm not an expert, but I think window.onload is a function that gets called, so you reset it to be another function (that you created). You're using it as a macro there. Since initialize_page does not exist when you passed it, it will probably get passed to window.onload as null.
I have the following piece of code on my page:
<script src="/Assets/JavaScripts/myJavaScript.js" type="text/javascript"></script>
<script type="text/javascript">
testAlert();
</script>
And in myJavaScript.js I have the following:
(function () {
function testAlert() {
alert('test alert');
}
})();
It's not calling testAlert. Not sure what I am doing wrong here? I'm trying to avoid global variables and methods. I did something similiar in jQuery and it worked, it just required a $ at the start of the external file. Can somebody please explain what I am doing wrong here and how to make sure I follow best practices?
I put your code in my environment and checked, I got error in mozila error console.
So, Please check it there.
you should put only below javascript function in myjavascript.js file.
function testAlert() {
alert('test alert');
}
Your function in the js-file isn't returning anything. To avoid global variables you could create one global namespace (-like) variable:
var myNS = (function () {
function testAlert() {
alert('test alert');
}
return {testAlert:testAlert};
}());
Now you can use myNS.testalert() in your inline javascript.
JS is made in such a way that you really can't totally avoid globals. (Well, you can, sort of. If you never give anything a name. But that tends to cause more problems than it solves.) When you call testAlert from some other script, you're operating under the assumption that testAlert is global. If it weren't, you couldn't just call it from anywhere like that.
You can minimize the chance of collision, though, by adding your stuff to an object that serves as a namespace, like so:
// don't clear it out if it already exists.
// that way all of your scripts can use your namespace, if you want.
// what you're really trying to protect against, are strangers picking names
// like yours.
if (!window.myNamespace) myNamespace = {};
// example function
myNamespace.testAlert = function() { alert("test alert"); };
myNamespace.testAlert();
This way, the only name that has a good chance of conflicting is myNamespace.
I was going to show an example of a namespaced global...but ya know what? A namespaced global is still a global. Global variables are something you want to try and get rid of in most cases.
You need some sort of global variable so that you can access the method you're looking for. The fact that you have <script>testAlert()</script> means you expect testAlert() to be defined in the global namespace.
What I like to do (especially when using YUI, which you have tagged this question with), is to create a global object that acts as a utility class.
var page = {
init: function() {
// Do some initialization...
},
testAlert: function() {
alert("Test Alert");
}
};
After you do that, you can use the single global "page" variable to access everything you need.
// e.g.
page.testAlert();
// or...
Y.on("domready", page.init, page);
Again, for the second example I'm assuming you're using YUI, since you tagged this question with it.