How can I access variables outside of current scope in javascript? - javascript

I'm writing an application in javascript and cannot figure it out how to access the variables declared in my function, inside this jquery parse. Inside I can access global variables, but I don't really want to create global vars for these values.
Basically I want to extract file names from an xml document in the simulationFiles variable. I check if the node attribute is equal with the simName and extract the two strings inside the xml elements, that part I think it's working.
How can I extract those xml elements and append them to local variables?
function CsvReader(simName) {
this.initFileName = "somepath";
this.eventsFileName = "somepath";
$(simulationFiles).find('simulation').each(function() {
if ($(this).attr("name") == simName) {
initFileName += $(this).find("init").text();
eventsFileName += $(this).find("events").text();
}
});
}

The this in the CsvReader function is not the same this in the each() callback (where instead it is the current element in the iteration). To access the scope of the outer function within the callback, we need to be able to reference it by another name, which you can define in the outer scope:
function CsvReader(simName) {
this.initFileName = "somepath";
this.eventsFileName = "somepath";
var self = this; // reference to this in current scope
$(simulationFiles).find('simulation').each(function() {
if ($(this).attr("name") == simName) {
// access the variables using self instead of this
self.initFileName += $(this).find("init").text();
self.eventsFileName += $(this).find("events").text();
}
});
}

I made a working demo (I changed it to use classes so it would work with HTML).
function CsvReader(simName) {
this.initFileName = "somepath";
this.eventsFileName = "somepath";
var context = this;
$(simulationFiles).find('simulation').each(function() {
if ($(this).attr("name") == simName) {
context.initFileName += $(this).find("init").text();
context.eventsFileName += $(this).find("events").text();
}
});
}

The simplest change you can do to make it work is... Change your function in each from normal ( function() {}) to arrow function ( () => {} ) that will automatically take the context of the function in which it is defined.

Related

td:nth-of-type('+element+') not working in casperjs

I want to get text of all td's inside the table with the class .fmBigTbl .
When I hard code the no's instead of giving piElements, for eg td:nth-of-type(2). The above works but when I use a variable instead of a number it outputs null.
How can I loop through all the td's in the table?
piElements = 1;
var data;
var count = 5;
this.repeat(count, function() {
this.then(function() {
data = this.evaluate(function() {
return $('.fmBigTbl').find('td:nth-of-type('+piElements+').fmLblCell2').text();
});
this.echo(data);
piElements++;
});
});
evaluate is sandboxed. The inside (page context) cannot simply access variables in outer scope (casper context). You need to pass piElements explicitly into evaluate:
data = this.evaluate(function(piElements) {
return $('.fmBigTbl').find('td:nth-of-type('+piElements+').fmLblCell2').text();
}, piElements);
From the docs:
Note: The arguments and the return value to the evaluate function must be a simple primitive object. The rule of thumb: if it can be serialized via JSON, then it is fine.
or you could simply use casper.fetchText:
data = this.fetchText('.fmBigTbl td:nth-of-type('+piElements+').fmLblCell2');

Prevent multiple DOM searches in jquery

I created a function to return me a jquery element.
function GetDialogButton() {
return $('a.dialog');
};
This was done as the same element was used within multiple other functions. I thought it best if it was obtained from a single place, therefore making it easier to change in future should the atribute name change.
I would like to improve this getter so that it does not perform a search everytime when called multiple times within a single page load.
How can I do this? do I cache it? or perhaps there is no need as this is optimised out?
You can create a cache variable, but it will pollute the global namespace again
var dialogButton;
function GetDialogButton() {
if(dialogButton){
return dialogButton;
}
dialogButton = $('a.dialog');
return dialogButton;
};
Creating a global cache variable is not necessary. You can do it without adding a variable to the global scope. Something like this would do:
var GetDialogButton = (function() {
var set;
return function() {
if (set === undefined) {
set = $('a.dialog');
}
return set;
};
}());
Well, you could lazy-load it.
var $dialogButton = null;
function GetDialogButton() {
if($dialogButton == null)
$dialogButton = $('a.dialog');
return $dialogButton
};
Another alternative, if you expect there to only be one dialog button you could give the element an id and then the act of searching for it will be more efficient
<a id="dialogButton">...</a>
$('#dialogButton')... // nice and quick
You could keep the global namespace clean by;
function GetDialogButton() {
if (typeof GetDialogButton.element === 'undefined' ) {
GetDialogButton.element = $("a.dialog");
}
return GetDialogButton.element;
};

Replace js namespaced global variable with js closure

I have some shared code in a single-page web application that is currently using a "globals" namespace to store a parameter as a global variable.
Using a namespace is an improvement over polluting the global "window" object, but it seems like this code is a good candidate for a closure to persist the value between invocations. I've messed around with some ideas but can't seem to get the syntax for a closure right.
Here's pseudo-code for the current version. All the code lives inside a "um" namespace. When my shared function is initially called by a new virtual page in my app, I need to store the contents of a JS object called 'extraData'. Subsequent invocations of the function don't have access to 'extraData', so I'm currently storing it in "um.globals.extraData" if underscore.js determines that the parameter is an object.
//***************************
// IMPLEMENTATION SAMPLE
//***************************
// Define namespaces (not showing: um.grid, um.ajax, um.classes, um.constants, etc.)
window.um = window.um || {};
um.globals = um.globals || {}; /* container for namespaced 'global' variables */
um.grid.loadOrUpdate = function (iOffset, isUpdate, extra) {
var ajaxParams = new um.classes.AjaxParams();
//-----
// If 'extra' is an object, store it in a global for subsequent invocations
if (_.isObject(extra)) {
// This seems like it could be a closure candidate...
um.globals.extraData = extra;
}
ajaxParams.values = [um.constants.urlPathParams.grid];
ajaxParams.verb = um.constants.httpVerbs.GET;
// Use the global variable 'extraData'
ajaxParams.extraData = um.globals.extraData;
um.ajax.callMessaging(ajaxParams);
};
And here's some pseudo-code for actually invoking the function:
//***************************
// INVOCATION SAMPLES
//***************************
// 1st invocation from virtual page 'Alpha'
um.grid.loadOrUpdate(0, false, { "alpha-key": "alpha-value" });
// 2nd invocation from virtual page 'Alpha'
um.grid.loadOrUpdate(1, true); // will re-use the "alpha" object
// 1st invocation from virtual page "Beta'
um.grid.loadOrUpdate(0, false, { "beta-key": "beta-value" });
// 2nd invocation from virtual page 'Beta'
um.grid.loadOrUpdate(1, true); // will re-use the "beta" object
How can I kill um.globals.extraData and replace this with some kind of closure inside of um.grid.loadOrUpdate?
EDIT
Here's some code from "JavaScript Patterns" that prompted me to ask this question:
var setup = function () {
var count = 0;
return function () {
return (count += 1);
}
};
// usage
var next = setup();
next(); // returns 1
next(); // returns 2
next(); // returns 3
To me, it's unclear what you're trying to achieve through closures. Closures allow you to encapsulate the state of variables within the current scope, which might be handy if you were trying to create various instances of your object, each with their own extra state.
You could do this by implementing loadOrUpdate in such a way that returns a reference to a function that can be called later. When said function is called, all the variables within that scope will be enclosed and retain the values from when the function was created.
For example:
um.grid.loadOrUpdate = function (iOffset, extra) {
var ajaxParams = new um.classes.AjaxParams();
//-----
ajaxParams.values = [um.constants.urlPathParams.grid];
ajaxParams.verb = um.constants.httpVerbs.GET;
um.ajax.callMessaging(ajaxParams);
// Return a function used to update this data later
return function (newOffset) // Update function
{
// From within here, you'll have access to iOffset and extra as they exist at this point
window.alert("Key: " + extra.key + " - Changing offset from " + iOffset + " to " + newOffset);
iOffset = newOffset;
};
};
You can then invoke your function like so, keeping in mind it will return a reference to a function:
var alpha = um.grid.loadOrUpdate(0, { "key": "alpha-value" });
var beta = um.grid.loadOrUpdate(0, { "key": "beta-value" });
When you call alpha() or beta(), the value of extra will be retained through a closure, thus there is no need to keep a global reference to it.
alpha(1); // Update from 0 to 1
alpha(2); // Update from 1 to 2
beta(3); // Update from 0 to 3
beta(4); // Update from 3 to 4
Example
However, if you're attempting to keep a single instance of extra that all calls to loadOrUpdate share, you'd probably be better off using your previous technique and just storing that current value as a property of the function itself, or anywhere else within the scope of that function.
Is this kind of approach what you're after?
var ns = {};
(function() {
var globals;
ns.func = function(update,opts) {
if(update)opts=globals;
else globals=opts;
console.log(opts);
}
})();
ns.func(false,"a");
ns.func(true);
ns.func(false,"b");
ns.func(true);
Output:
a
a
b
b
I've scoped the globals variable inside an anonymous function, and made a function declared in that function available on an object in the surrounding (in this case window) scope - so it has access to the 'globals' variable but it's not visible outside it.

Replacing window with custom javascript global namespace

I have a javascript function the initializes a bunch of global varaibles for a game.
function buildVariables(fs,fm) {
window.p1HPStart = fm.p1hp;
window.p2HPStart = fm.p2hp;
window.p1HP = 100;
window.p2HP = 100;
window.trn = 0;
}
Right now all this javascript is in the same HTML file. I want to move it to its own .js file and include it in this HTML file. I also want to replace "window" with a different global namespace like fight.p1HP.
How can I do this?
I've seen code like the below as a proposed answer in other similar questions, but I don't quite understand how it can be used to replace window.
var cartTotaler = (function () {
var total = 0; tax = 0.05;
// other code
return {
addItem : function (item) { },
removeItem : function (item) { },
calculateTitle : function () { }
};
}());
Thanks.
// initialize your own global object
if (!window.mySpace) {
window.mySpace = {};
}
// then use it
function buildVariables(fs,fm) {
mySpace.p1HPStart = fm.p1hp;
mySpace.p2HPStart = fm.p2hp;
mySpace.p1HP = 100;
mySpace.p2HP = 100;
mySpace.trn = 0;
}
Then just make sure everywhere you want one of your own variables, you use your namespace in front of it:
mySpace.variableName
Note: this doesn't really "replace" the window object (as there is no way to do that) - it just puts all your global variables into one master global object rather than pollute the global namespace with every single one of your variables.
The name mySpace can be anything you want it to be. Typically, it should be something that is unique to your application that is unlikely to conflict with something any other javascript or library might use.
(function(global){
global.p1HPStart = fm.p1hp;
global.p2HPStart = fm.p2hp;
global.p1HP = 100;
global.p2HP = 100;
global.trn = 0;
}(window));
This creates an 'immediately invoked function expression'. window is passed into the function, which then attaches a number of properties to it.
You can change window to whatever object you want, such as fight.p1HP, and this function will immediately attach the listed properties to that object.

how to get the value from a callback function

I am relatively new to javascript and I am facing some difficulty.I have two java script files as I have shown below. I am having trouble getting the value of the variable entry_title inside the getRss function and storing it inside the variables Rss1_title and Rss2_title . Creating a global variable and assigning it to entry_title will make things worse as I will not be able to know from which Rss url the title came from. Is there a easy way to get the value of the callback functions ?
<script type="text/javascript" src="jsRss.js"></script>
<script type="text/javascript" src="notification.js"></script>
My notification.js file
function get_rss1_feeds(){
var Rss1_title = getRss("http://yofreesamples.com/category/free-coupons/feed/?type=rss");
}
function get_rss2_feeds(){
var Rss2_title = getRss("http://yofreesamples.com/category/real-freebies/feed/?type=rss");
}
setTimeout('get_rss1_feeds()',8000);
setTimeout('get_rss2_feeds()',7000);
My jsRss.js file:
function getRss(url){
if(url == null) return false;
google.load("feeds", "1");
// Our callback function, for when a feed is loaded.
function feedLoaded(result) {
if (!result.error) {
var entry = result.feed.entries[0];
var entry_title = entry.title; // need to get this value
}
}
function Load() {
// Create a feed instance that will grab feed.
var feed = new google.feeds.Feed(url);
// Calling load sends the request off. It requires a callback function.
feed.load(feedLoaded);
}
google.setOnLoadCallback(Load);
}
Errors :
When the setTimeout(get_rss1_feeds, 8000); method is called I get a blank screen.
I get a error in my console saying octal literals and octal escape sequences are deprecated and it is pointing to the 6th line in this script.
Is it because I am using google-api for parsing my Rss?
if (window['google'] != undefined && window['google']['loader'] != undefined) {
if (!window['google']['feeds']) {
window['google']['feeds'] = {};
google.feeds.Version = '1.0';
google.feeds.JSHash = '8992c0a2cdf258e5bd0f517c78243cd6';
google.feeds.LoadArgs = 'file\75feeds\46v\0751';
}
google.loader.writeLoadTag("css", google.loader.ServiceBase + "/api/feeds/1.0/8992c0a2cdf258e5bd0f517c78243cd6/default+en.css", false);
google.loader.writeLoadTag("script", google.loader.ServiceBase + "/api/feeds/1.0/8992c0a2cdf258e5bd0f517c78243cd6/default+en.I.js", false);
}
Seeing as it's a different scope, you can either return it in a callback, or provide it in another way such as exporting it to a higher scope that is visible to your desired location. In this case, it's the global scope, so I'd advise against that.
function getRss(url, callback) {
//...
function feedLoaded(result) {
if (!result.error) {
var entry = result.feed.entries[0];
var entry_title = entry.title; // need to get this value
callback && callback(entry_title);
}
}
and call it like so,
function get_rss1_feeds() {
var Rss1_title = getRss("http://yofreesamples.com/category/free-coupons/feed/?type=rss", function(entry_title) {
// This scope has access to entry_title
});
}
As an aside, use your setTimeout like so:
setTimeout(get_rss1_feeds, 8000);
rather than
setTimeout("get_rss1_feeds()", 8000);
as the latter uses eval, whereas the former passes a reference to the function.
Eventhough it will make your code a mess, you can append the variables to the window object.
For example:
function a()
{
window.testStr = "test";
}
function b()
{
alert(window.testStr);
}
Or even create your own object, instead of using window, as such:
var MyRSSReader = {
TitleOne : '',
TitleTwo : ''
}
MyRSSReader.TitleOne = "My title";
Wikipedia has a nice article about global variables, and why they are bad.

Categories