I try to submit a simple Firefox add-on and got a message from AMO editor about wrapping of variables and functions within a JavaScript object in order to prevent conflicts with other add-ons that may be installed by users. The working code is very simple and looks:
function analyze() {
var uri = document.getElementById('urlbar').value;
var requrl="http://www.myanalyzingsiteaddress.com/" + (uri);
gBrowser.selectedTab = gBrowser.addTab(requrl);
}
Is it enough to make other var names to avoid eventual conflicts or could you point me to other code change, which would fulfill the AMO editor's instruction?
Thank you in advance!
Evgenij
You should have been pointed to Javascript Object Management from the XUL School tutorial.
analyze is a generic name. In an overlay there is only one scope/namespace which is shared by the browser code itself and any additional extension code. It is therefore possible that either the browser or another add-on uses analyze as well and boom.
You need to avoid that by making names a specifc as possible. E.g.
function my_addon_id_analyze() ...
Use an an object with a (pseudo) unique name.
if (!("org" in this)) {
this.org = {};
}
if (!("example" in org)) {
org.example = {};
}
org.example.addonid = {
analyze: function() ...
};
// call
org.example.addonid.analyze();
Or even "hide" your code in an anonymous function. This is then hidden from the DOM as well, so no more <button id="example.org.addonid.mybutton" onclick="analyze()"> event handling. But you can always use addEventListener.
(function() {
"use strict";
function analyze() ...
// wire up an event handler instead of onlick
document.getElementById("example.org.addonid.mybutton").addEventListener("click", analyze);
})();
Mix some/all of the above, as long as you avoid short/generic names:
if (!("org" in this)) {
this.org = {};
}
if (!("example" in org)) {
org.example = {};
}
org.example.addonid = (function() {
function analyze() ...
function notvisibleoutside() ...
// return object of "exported"/"visible" functions.
return {
analyze: analyze
};
})();
// call
org.example.addonid.analyze()
Also, keep in mind that missing var (or let/const) declarations will implicitly declare the variable in the global scope.
E.g.:
function abc() {
for (i = 0; i < 10; ++i) doSomething();
}
Will implicitly declare a variable named i in the global scope (in a XUL window the global scope is window, therefore this will create window.i). Implicit declarations may therefore not only cause conflicts but also create quasi memory leaks, e.g.
function call_me_to_leak_1MB() {
hugeArray = new ArrayBuffer(1<<20);
}
will declare window.hugeArray that lives as long as the browser window is open instead of using a local variable that gets garbage collected as soon as the variables goes out of scope (and there are no more other references, of course).
Using strict mode makes implicit declaration an error, which is helpful to catch and avoid such mistakes early.
So much for the Javascript part. There is still other stuff that might clash.
DOM Ids: Use unique <button id="example.org.addonid.mybutton">, or more CSS friendly <button id="example-org-addonid-mybutton"> (or at the very least something like addonid-mybutton) instead of <button id="mybutton">
CSS: Never style random elements.
No: button { color: green; }
Yep: #example-org-addonid-mybutton { color: green; }
chrome.manifest:
No: content generic content/
Yep: content example-org-addonid content/.
Related
I Have this JavaScript code:
var cr = {};
cr.plugins_ = {};
cr.runtime = null;
cr.plugins_.Vinoos_Markets = function(runtime) {
this.runtime = runtime;
};
(function() {
function initialize_events(result) {
alert(result);
}
})();
<button onclick="initialize_events('Test Result');">Send Result</button>
how to run 'initialize_events' function from html by clicking on button?
I don't have access to editing JavaScript file.
i dont have access to editing js file.
Then you can't, full stop. It's entirely private to the anonymous IIFE* that encloses it. You'd have to expose it as a global in order to use it with an onxyz-attribute-style event handler (and that would require modifying the JavaScript code). It's one of the many reasons not to use them.
Since you can't do it without modifying the JavaScript, I'm going to assume you overcome that limitation and suggest what to do when/if you can modify the JavaScript:
Have that IIFE hook up the button, and use a data-* attribute if you need button-specific information to pass it:
var cr = {};
cr.plugins_ = {};
cr.runtime = null;
cr.plugins_.Vinoos_Markets = function(runtime) {
this.runtime = runtime;
};
(function() {
function initialize_events(result) {
alert(result);
}
document.getElementById("send-result").addEventListener("click", function() {
initialize_events(this.getAttribute("data-result"));
}, false);
}());
<button id="send-result" data-result="Test Result">Send Result</button>
Notes:
If you need to support obsolete browsers without addEventListener (such as IE8, which is sadly still a requirement for many), see this answer for a cross-browser event hooking function.
If you have the data in the IIFE rather than the button, you can just use it directly rather than using a data-* attribute.
Giving the button an ID and using getElementById is just an example; in practice, anything that lets you identify the button is all you need. You can look up using a full CSS selector via document.querySelector.
* IIFE = immediately-invoked function expression, e.g., (function() { /*...*/})(); (Also sometimes called an "inline-invoked function expression." Also sometimes erroneously called a "self-invoking function," but it isn't; it's invoked by the code defining it, not by the function itself.)
I have a functionality that I had running in the
window.addEventListener('load', function() {
var variable_name_1 = localStorage.getItem('var_1');
...
}
and I would like to move the functionality such that it only runs when the user clicks a button, in here:
function maketempuser() {
...
}
I can get the function to call when I want. But the function utilizes tons of variables from the load function. Is there a clean way to "globalize" these variables? Or must I find some way to add all these variables in the html:
<button ... onclick='maketempuser(variable_name_1, variable_name_2, ...);' >
NOTE: the javascript will run the same file, I just don't want it to keep re-running every time the user reloads the page since there is an ajax mysql insert that occurs because this page is one in a line of pages that enables a user to register.
To not pollute the global scope with a lot of variables (which can be overridden by other apps), I recommend you create an object with an app specific name, maybe something like this
var myAppVar = {};
window.addEventListener('load', function() {
myAppVar.var_1 = localStorage.getItem('var_1');
...
}
Just define them in global scope:
var variable_name_1;
window.addEventListener('load', function() {
variable_name_1 = localStorage.getItem('var_1');
...
}
This, however, is not a particularly healthy technique, since it's prone to name collisions. Best thing to do is have a custom object (cO, or with your initials, something unlikely to be used by anything else) and use it as a placeholder for all your custom vars:
var cS = {
var_1:null // or some default value...
};
window.addEventListener('load', function() {
cS.var_1 = localStorage.getItem('var_1');
...
}
Since localStorage is already global just retrieve the values you need in your handler from there.
function maketempuser() {
var variable_name_1 = localStorage.getItem('var_1');
}
No need to add anything extra to the global scope at all.
Thus far I've worked only with relatively small projects (and mostly alone), but this time I have to collaborate with other programmers... basically because of that I must plan the structure of the website very carefully for the avoidance of spending hours debugging the code.
At this point I suppose doing that in the following manner. I divide my code in modules and store each module in a separate file inside an object (or a function) with a made-up name (lzheA, lzheB, lzheC etc.) to avoid conflicts whether an object with the same name was used in an another piece of code. When the document is loaded, I declare a variable (an object) that I use as a main namespace of the application. Properties of the object are the modules I defined before.
// file BI.lib.js
var lzheA = {
foo: function() {
},
bar: function() {
},
}
// file BI.init.js
function lzheK() {
BI.loadPage();
}
// file BI.loadPage.js
function lzheC() {
var result = document.getElementById('result');
result.innerHTML = "that worked";
}
// and so on
var lzheA,lzheB,lzheD,lzheE,lzheF,lzheG,lzheH,lzheI,lzheJ;
// doing the following when the document is loaded
var BI = {
lib: lzheA,
menu: lzheB,
loadPage: lzheC,
customScripts: lzheD,
_index: lzheE,
_briefs: lzheF,
_shop: lzheG,
_cases: lzheH,
_blog: lzheI,
_contacts: lzheJ,
init: lzheK,
}
BI.init();
https://jsfiddle.net/vwc2og57/2/
The question... is this way of structuring worth living or did I miss something because of lack of experience? Would the made-up names of the modules confuse you regardless of the fact that each one used only twice - while declaring the variable and assigning it to a property?
I consider the namespaces a good option when you want to modularize applications in Javascript. But I declare them in a different way
var myModule = myModule || {}; // This will allow to use the module in other places, declaring more than one specificComponent in other js file for example
myModule.specificComponent = (function(){
// Private things
var myVar = {};
var init = function() {
// Code
};
return {
init: init // Public Stuff
};
})();
If you want to call the init method, you would call it like this
myModule.specificComponent.init();
With this approach, i guarantee that the module will not be overwritten by another declaration in another place, and also I can declare internal components into my namespaces.
Also, the trick of just exposing what you want inside the return block, will make your component safer and you will be encapsulating your code in a pretty way.
Hope it helps
I need a few global variables that I need in all .js files.
For example, consider the following 4 files:
global.js
js1.js
js2.js
js3.js
Is there a way that I can declare 3 global variables in global.js and access them in any of the other 3 .js files considering I load all the above 4 files into a HTML document?
Can someone please tell me if this is possible or is there a work around to achieve this?
Just define your variables in global.js outside a function scope:
// global.js
var global1 = "I'm a global!";
var global2 = "So am I!";
// other js-file
function testGlobal () {
alert(global1);
}
To make sure that this works you have to include/link to global.js before you try to access any variables defined in that file:
<html>
<head>
<!-- Include global.js first -->
<script src="/YOUR_PATH/global.js" type="text/javascript"></script>
<!-- Now we can reference variables, objects, functions etc.
defined in global.js -->
<script src="/YOUR_PATH/otherJsFile.js" type="text/javascript"></script>
</head>
[...]
</html>
You could, of course, link in the script tags just before the closing <body>-tag if you do not want the load of js-files to interrupt the initial page load.
The recommended approach is:
window.greeting = "Hello World!"
You can then access it within any function:
function foo() {
alert(greeting); // Hello World!
alert(window["greeting"]); // Hello World!
alert(window.greeting); // Hello World! (recommended)
}
This approach is preferred for two reasons.
The intent is explicit. The use of the var keyword can easily lead to declaring global vars that were intended to be local or vice versa. This sort of variable scoping is a point of confusion for a lot of Javascript developers. So as a general rule, I make sure all variable declarations are preceded with the keyword var or the prefix window.
You standardize this syntax for reading the variables this way as well which means that a locally scoped var doesn't clobber the global var or vice versa. For example what happens here is ambiguous:
greeting = "Aloha";
function foo() {
greeting = "Hello"; // overrides global!
}
function bar(greeting) {
alert(greeting);
}
foo();
bar("Howdy"); // does it alert "Hello" or "Howdy" ?
However, this is much cleaner and less error prone (you don't really need to remember all the variable scoping rules):
function foo() {
window.greeting = "Hello";
}
function bar(greeting) {
alert(greeting);
}
foo();
bar("Howdy"); // alerts "Howdy"
Have you tried it?
If you do:
var HI = 'Hello World';
In global.js. And then do:
alert(HI);
In js1.js it will alert it fine. You just have to include global.js prior to the rest in the HTML document.
The only catch is that you have to declare it in the window's scope (not inside any functions).
You could just nix the var part and create them that way, but it's not good practice.
As mentioned above, there are issues with using the top-most scope in your script file. Here is another issue: The script file might be run from a context that is not the global context in some run-time environment.
It has been proposed to assign the global to window directly. But that is also run-time dependent and does not work in Node etc. It goes to show that portable global variable management needs some careful consideration and extra effort. Maybe they will fix it in future ECMS versions!
For now, I would recommend something like this to support proper global management for all run-time environments:
/**
* Exports the given object into the global context.
*/
var exportGlobal = function(name, object) {
if (typeof(global) !== "undefined") {
// Node.js
global[name] = object;
}
else if (typeof(window) !== "undefined") {
// JS with GUI (usually browser)
window[name] = object;
}
else {
throw new Error("Unkown run-time environment. Currently only browsers and Node.js are supported.");
}
};
// export exportGlobal itself
exportGlobal("exportGlobal", exportGlobal);
// create a new global namespace
exportGlobal("someothernamespace", {});
It's a bit more typing, but it makes your global variable management future-proof.
Disclaimer: Part of this idea came to me when looking at previous versions of stacktrace.js.
I reckon, one can also use Webpack or other tools to get more reliable and less hackish detection of the run-time environment.
Yes you can access them. You should declare them in 'public space' (outside any functions) as:
var globalvar1 = 'value';
You can access them later on, also in other files.
Are there any dangers/caveats one should be aware of when creating JavaScript namespaces?
Our project is fairly expansive and we are running a lot of JavaScript files (20+, expecting more). It is impossible to have any code maintainability without using namespaces, so we are implementing them like so:
var namespace1 = {
doSomething: function() {
...
},
doSomethingElse: function() {
...
}
}
And then to create hierarchies, we link them like so:
var globalNamespace = {
functions1: namespace1,
functions2: namespace2,
...
}
This works fine, but it is essentially a "trick" to make JS behave as if it did have namespaces. Although this method gets used a lot, most literature on this seems to focus on how to do it, and not whether there are any possible drawbacks. As we write more JS code, this is quickly becoming an integral part of the way our system works. So it's important that it works seamlessly.
Were there any situations in which this "induced" namespace system caused you errors, or otherwise needed special attention? Can we safely expect identical behaviour across all browsers?
The way you define namespaces in your example it appears to create globals out of each namespace so you end up with
window.namespace1
window.namespace2
window.globalNamespace
window.globalNamespace.namespace1
window.globalNamespace.namespace2
So if you have anything that clobbers window.namespace1 it will also clobber window.globalNamespace.namespace1
edit:
Here's how we got around this problem:
namespacing = {
init: function(namespace) {
var spaces = [];
namespace.split('.').each(function(space) {
var curSpace = window,
i;
spaces.push(space);
for (i = 0; i < spaces.length; i++) {
if (typeof curSpace[spaces[i]] === 'undefined') {
curSpace[spaces[i]] = {};
}
curSpace = curSpace[spaces[i]];
}
});
}
};
Then you use it like this:
namespacing.init('globalNamespace.namespace1');
globalNamespace.namespace1.doSomething = function() { ... };
This way you don't have to introduce new global variables and you can confidently add to an existing namespace without clobbering other objects in it.
Since you are basically adding functions to objects and those objects into other objects, I would expect each browser to handle this the same way.
But if you want modularity, why not use a (relatively) simple framework like require.js? That will allow you and your team to write code in a modular fashion and allows the team to 'import' these modules where needed:
require(["helper/util"], function() {
//This function is called when scripts/helper/util.js is loaded.
});
Require.js will take care of dependencies, and it will also prevent polluting the global namespace.
We use a similar system at work and it does the job just fine. I don't see any drawbacks there could be; it's just objects and properties. For that same reason, cross browser compatibility should be good. You can end up having to write some long names to resolve to a particular function, like Foo.Bar.Test.Namespace2.Function, but even then that can be solved by assigning it to a variable before hand.
This is how I'd recommend doing it, so you stay out of the global scope entirely except for your "base" namespace. We do something similar where I work. Let's say you work for Acme co, and want ACME to be your base namespace.
At the top of every file, you'd include:
if (!window.ACME) { window.ACME = {} }
Then you just go and define whatever you want in terms of that.
ACME.Foo = {
bar: function () { console.log("baz"); }
}
If you want a deeper level of namespace, you just do the same thing for each level.
if (!window.ACME) { window.ACME = {} }
if (!ACME.Foo) { ACME.Foo = {} }
This way each file can be tested independently and they'll set up the namespace infrastructure automatically, but when you compile them together or if you test multiple files simultaneously, they won't keep overwriting things that are already defined.