Javascript scope issue, inside an anonymous function - javascript

Sorry I couldn't be anymore specific with the title.
I'm building a web-site (personal), which displays different content to the user depending on the query string that is used in the url.
e.g. page=home.html would display home.html
The websites Javascript is wrapped inside an object, with each value containing different data, some pseudo code:
(function(){
var wrapper = {
init: function(){
//Runs on document ready
this.foo();
this.nav.render();
},
foo: function(){
//Some functionality goes here for the website, e.g. Display something from an API
},
nav: {
//Functionality to handle the navigation, has different properties
config: {
//Contains the config for nav, e.g. page names + locations
dir: '/directory/to/content/',
pages: {
page_name: wrapper.nav.config.dir + 'page_value'
}
},
render: function(){
//some code
},
routes: function(){
//some code}
}
}
};
$(function(){
wrapper.init();
});
})();
My problem is that I'm trying to prepend the dir value to each of the page values (inside the object where the pages are defined), expecting to get the output of (in this pseudo code case) of directory/to/content/page_value, but instead dir is undefined when I'm trying to access it, I've tried the following to achieve what I want:
wrapper.nav.config.dir + 'page_value'
I've been playing around with the last 30 minutes trying to find out what I'm doing wrong, and even thought about hard-coding the URL in for each page.
The reasoning for wanting to do this is that my local development server and web host have different directory structures, so I don't want to re-write the URL's each time I want to develop + publish. As for why everything is wrapped inside an object, I thought it would be easier to maintain this way.
Hopefully the answer is simple and it's just an amateur mistake / lack of understanding.

The issue is that you can't refer to a variable that is being defined in that very definition.
So, inside the definition of wrapper, you can't refer to wrapper. And, inside the definition of config, you can't refer to config either and so on.
The usual design pattern for solving this is to initialize as much as you can in the declaration of your data structure and then do the rest in .init() when you can freely access all of it.

Change the first two lines to:
var wrapper = null;
(function(){
wrapper = {
Otherwise, the wrapper is a local variable to your anonymous function.

The problem is that you're still busy defining the wrapper when you ask for its value, which is why it's still undefined.
The code below fails too:
var x = {
y:"1",
z:x.y
}

Why not:
//...
init: function(){
//Runs on document ready
this.foo();
var config = this.nav.config;
for (var page in config.pages) {
config.pages[page] = config.dir + config.pages[page];
}
},
//...

Related

how to cleanly use JS variables in an onclick function that are set in the page load function

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.

interacting with javascript through the chrome console [duplicate]

This question already has answers here:
Console access to Javascript variables local to the $(document).ready function
(8 answers)
Closed 6 years ago.
I'm using a shopping cart api to build an ecommerce website. The creators made an sdk and then you have to make your own .js file for some other functions.
While debugging I would insert a console.log(etc..) anywhere in my .js file so that I could debug object options and etc..
But I would like to be able to use the sdk as a live tool, so instead of having to edit my .js file with new console.log() lines, I'd rather just be able to type object.color_code and have the console output that string for the object color code. At the moment though it just gives me uncaught reference error, object is not defined.
I think this is because my custom .js file has all of it's script inside a $(function() { EVERYTHING }); SO, when I try to call anything in EVERYTHING from the console it says it's undefined, but if I just used console.log inside EVERYTHING it would work. So is there a way I can get around this?
Feel free to explain why it isn't working but I'd like a way to enable this, don't tell me there isn't a way, even if I have to prefix what I want with the .js file it's coming from each time, I don't mind
You were correct in that all of your variables inside the function are only being defined locally, and thus can't be accessed via the console. However, in Javascript there are at least two options for setting global variables from inside functions; If you use these to declare a variable you want to access from outside the function, it will work:
Assign a value to an undeclared variable: varname=value;
Assign the variable to the window object: window.varname=value; or window['varname']=value;
A possible workaround is to expose the object(s) that you want to debug in the global scope:
(function() {
var privateStuff = { foo: 'bar' };
// make privateStuff public for debugging purposes
window['debugObject'] = privateStuff;
})();
document.write(debugObject.foo);
If you want to expose several objects with rather common names that are likely to collide with existing ones, make sure to expose them within an object with an uncommon name rather than directly:
(function() {
var x = { str: 'this is' },
y = { str: 'a test' };
window['debugObject'] = {
x: x,
y: y
};
})();
document.write(debugObject.x.str + ' ' + debugObject.y.str);
If you're happy to change the source file then you could export whatever you want to access from EVERYTHING as a global.
$(function() {
//EVERYTHING
...
window.Ireply = window.Ireply || {};
window.Ireply.object = object;
...
});
console.log(Ireply.object); // some object
You can change a declaration like
$(function(){
var cart = {};
})
To
var cart;
$(function(){
cart = {}
})
Or
$(function(){
var cart = {};
window.cart = cart;
})
But you will want to avoid polluting global namespace. You will also want to be careful about using globals inside callbacks or loops where you can run into unexpected behaviors since local variables scope is often important to be kept local

js - avoiding namespace conflict

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

Additional url attribute at nightwatch page object

I was trying to add an additional url attribute as a function to my page-object while using nightwatchjs.
Like:
module.exports = {
url: function() {
return this.api.launchUrl + '/content/site1.xhtml';
},
cancelUrl: function() {
return this.api.launchUrl + '/content/cancel_site1.xhtml';
}
}
Anyhow nightwatch is not able to get that 2nd attribute cancelUrl, ie undefined.
Why is that so? Shouldn't nightwatch be able to access that attribute as it is nothing more than a function call returning a string or am I misunderstanding a javascript or special page-object concept?
--
I am aware that there should be a page-object for each site so there should not be a 2nd site. Anyhow I would like to understand why this is not working technically.
Not sure I can answer the "why" (other than to say that when nightwatch loads up your page objects as globally available it must be wrapping your js file and filtering on 'known' functions) but I can offer a solution: add a command to your page object with the desired function. For example:
let pageCommands = {
cancelUrl: function() {
return this.api.launchUrl + '/content/cancel_site1.xhtml';
}
};
module.exports = {
commands: [pageCommands],
...
}
It's not the typical use of page commands, but your test would then be able to access the cancelUrl function on the page object instance.
More on page commands here

understanding a modular javascript pattern

I'm trying to write 'better' javascript.
Below is one pattern I've found, and am trying to adopt. However, I'm slightly confused about its use.
Say, for example, I've got a page called "Jobs". Any JS functionality on that page would be encapsulated in something like:
window.jobs = (function(jobs, $, undefined){
return {
addNew: function(){
// job-adding code
}
}
})(window.jobs|| {}, jQuery);
$(function(){
$('.add_job').on('click', function(event){
event.preventDefault();
window.jobs.addNew();
});
});
As you can probably deduct, all I've done is replaced all the code that would have sat inside the anonymous event-handler function, with a call to a function in my global jobs object. I'm not sure why that's a good thing, other than it's reduced the possibility of variable collisions and made the whole thing a bit neater, but that's good enough for me.
The - probably fairly obvious - question is: all my event-binding init-type stuff is still sitting outside my shiny new jobs object: where should it be? Inside the jobs object? Inside the return object inside the jobs object? Inside an init() function?
I'm just trying to get a sense of a stable, basic framework for putting simple functionality in. I'm not building JS apps, I'd just like to write code that's a little more robust and maintainable than it is currently. Any and all suggestions are warmly welcomed :)
You can break down your application in whatever number of modules / objects you like too.
For instance, you can have another object / module which caches and defines all your DOM nodes and another one, which just handles any event. So for instance:
(function ( win, doc, $, undef ) {
win.myApp = win.myApp || { };
var eventHandler = {
onJobClick: function( event ) {
event.preventDefault();
myApp.addNew();
}
};
var nodes = (function() {
var rootNode = $( '.myRootNode' ),
addJob = rootNode.find( '.add_job' );
return {
rootNode: rootNode,
addJob: addJob
};
}());
$(function() {
myApp.nodes.addJob.on( 'click', myApp.handler.onJobClick );
});
myApp.nodes = nodes;
myApp.handler = eventHandler;
}( this, this.document, jQuery ));
It doesn't really matter how you create singletons in this (module) pattern, either as literal, constructor, Object.create() or whatnot. It needs to fit your requirements.
But you should try to create as many specific modules/objects as necesarry. Of course, if makes even more sense to separate those singletons / modules / objects into multiple javascript files and load them on demand and before you can say knife, you're in the world of modular programming patterns, dealing with requireJS and AMD or CommonJS modules.
Encapsulation-wise, you're fine: you could even just declare addNew in the jQuery closure and you'd still avoid the global scope. I think what you're getting at is more of implementing something close to an MVC architecture.
Something I like to do is create an object that you instantiate with a DOM element and that takes care of its own bindings/provides methods to access its controls etc.
Example:
// (pretend we're inside a closure already)
var myObj = function(args){
this.el = args.el; // just a selector, e.g. #myId
this.html = args.html;
this.bindings = args.bindings || {};
}
myObj.prototype.appendTo = function(elem){
elem.innerHTML += this.html;
this.bindControls();
};
myObj.prototype.remove = function(){
$(this.el).remove(); // using jQuery
};
myObj.prototype.bindControls = function(){
for(var i in this.bindings){ // event#selector : function
var boundFunc = function(e){ return this.bindings[i].call(this,e); };
$(this.el).on(i,boundFunc);
}
};
The way you are doing it right now is exactly how I do it also, I typically create the window objects inside the anonymous function itself and then declare inside that (in this case: jClass = window.jClass).
(function (jClass, $, undefined) {
/// <param name="$" type="jQuery" />
var VERSION = '1.31';
UPDATED_DATE = '7/20/2012';
// Private Namespace Variables
var _self = jClass; // internal self-reference
jClass = window.jClass; // (fix for intellisense)
$ = jQuery; // save rights to jQuery (also fixes vsdoc Intellisense)
// I init my namespace from inside itself
$(function () {
jClass.init('branchName');
});
jClass.init = function(branch) {
this._branch = branch;
this._globalFunctionality({ globalDatePicker: true });
this._jQueryValidateAdditions();
//put GLOBAL IMAGES to preload in the array
this._preloadImages( [''] );
this._log('*******************************************************');
this._log('jClass Loaded Successfully :: v' + VERSION + ' :: Last Updated: ' + UPDATED_DATE);
this._log('*******************************************************\n');
};
jClass._log = function() {
//NOTE: Global Log (cross browser Console.log - for Testing purposes)
//ENDNOTE
try { console.log.apply(console, arguments); }
catch (e) {
try { opera.postError.apply(opera, arguments); }
catch (e) { /* IE Currently shut OFF : alert(Array.prototype.join.call(arguments, ' '));*/ }
}
};
}(window.jClass= window.jClass|| {}, jQuery));
The reason I leave them completely anonymous like this, is that let's say in another file I want to add much more functionality to this jClass. I simply create another:
(function jClass, $, undefined) {
jClass.newFunction = function (params) {
// new stuff here
};
}(window.jClass = window.jClass || {}, jQuery))
As you can see I prefer the object.object notation, but you can use object literals object : object, it's up to you!
Either way by leaving all of this separate, and encapsulated without actual page logic makes it easier to have this within a globalJS file and every page on your site able to use it. Such as the example below.
jClass._log('log this text for me');
You don't want to intertwine model logic with your business logic, so your on the right path separating the two, and allowing for your global namespace/class/etc to be more flexible!
You can find here a comprehensive study on module pattern here: http://www.adequatelygood.com/JavaScript-Module-Pattern-In-Depth.html It covers all the aspects of block-scoped module approach. However in practice you gonna have quite a number files encapsulating you code, so the question is how to combine them property. AMD... multiple HTTP requests produced by every module loading will rather harm your page response time. So you can go with CommonJS compiled to a single JavaScript file suitable for in-browser use. Take a look how easy it is http://dsheiko.github.io/cjsc/

Categories