How can I use multiple variables to retrieve JS objects? - javascript

I'm only working on my 3rd Javascript project, so this is probably easy to answer (at least I hope so).
I have learned to use JS object in place of arrays. In this project I have named multiple object with a nested system of IDs as follows:
animalia = new Object();
animalia.chordata = new Object();
animalia.chordata.actinopterygii = new Object();
animalia.chordata.actinopterygii.acipenseriformes = new Object();
etc.......
I'm having problems calling on objects named this way though. Here is my code:
function expand(event){
var target = event.target;
console.log(target);
var parent = target.parentNode;
console.log(parent);
var parentclass = parent.getAttribute("class");
console.log(parentclass);
if (parentclass == "kingdom"){
var newdiv = document.createElement("div");
var newexpctrl = document.createElement("div");
var parentid = parent.getAttribute("id");
console.log(parentid);
----> var parentobj = window[parentid];
console.log(parentobj);}
else{
var upperclass = searchArray(parentclass);
console.log(upperclass);
var newdiv = document.createElement("div");
var newexpctrl = document.createElement("div");
var parentId = parent.getAttribute("id");
console.log(parentId);
var parentnode_ = document.getElementById(parentId);
console.log(parentnode_);
var gparentId = parentnode_.parentNode.id;
console.log(gparentId);
----> var parentobj = window[gparentId.parentId];
console.log(parentobj);
}
var childnumb = parentobj.children;
}
I am having my problem with the two statements indicated by "---->". In the first case, using a single variable works for pulling up the proper object. However, in the second case, using two variables, I fail to be able to access the proper object. What is the proper syntax for doing this? I have tried a plethora of different syntax combinations, but nothing seems to work correctly. Or should is there a better method for calling on JS objects other than using "window[variable]"?
P.S.- If you haven't figured it out by now, I am working on educational tools for use in learning biology. Thanks once again stackoverflow, you guys rule.

Assuming that the window object has something w/ the property matching a string that's the value of gparentId, you should be able to do:
var parentobj = window[gparentId][parentId];

The problem here is that the square bracket's notation is being applied to too much. gparentId is a string. It doesn't have a property called parentId. You therefore have to do this in two steps. First get:
window[gparentId]
Then get the appropriate property of that object
var parentobj = window[gparentId][parentId];
On a somewhat unrelated note, this isn't very well written JavaScript code:
Creating Objects
When creating new objects, always use the following syntax:
var obj = {};
That's what's generally been accepted as standard, so it's easier for people to read.
Declaring Variables in If Statements
You shouldn't really declare variables inside an if statement, especially when declaring the same variable in the else block, that's really confusing. Instead, declare all the variables at the top in a list and then use them without the var keyword lower down.
var newdiv = document.createElement("div"),
newexpctrl = document.createElement("div"),
parentid = parent.getAttribute("id"),
parentobj;
Note the commas instead of semi-colons which means I don't have to repeat the var keyword. Since the values of newdiv, newexpctrl and parentid are the same in either case, I give them their values straight away, making the contents of the if statement much shorter and easier to digest.
Result
function expand(event){
var target = event.target;
var parent = target.parentNode;
var parentclass = parent.getAttribute("class");
var newdiv = document.createElement("div"),
newexpctrl = document.createElement("div"),
parentid = parent.getAttribute("id"),
parentobj, upperclass;
if (parentclass == "kingdom"){
parentobj = window[parentid];
}else{
upperclass = searchArray(parentclass);
var _parentId = document.getElementById(parentId).parentNode.id;
parentobj = window[_parentId][parentId];
}
var childnumb = parentobj.children;
}
Note that I've left var _parentId inside the if since I think it probably improves readability, but you may choose to take it outside the if, since it will pollute the namespace of the function anyway.

Related

textContent Vs. innerText Cross Browser solution

I've been having a hard time with cross browser compatibility and scrapping the dom.
I've added data analytics tracking to ecommerce transactions in order to grab the product and transaction amount for each purchase.
Initially I was using document.querySelectorAll('#someId')[0].textContent to get the product name and that was working fine for every browser except internet explorer.
It took some time to figure out that it was the .textContent part that was causing ie problems.
Yesterday I changed .textContent to .innerText. From looking inside analytics it seems that the issue has been resolved for ie but now Firefox is failing.
I was hoping to find a solution without writing an if statement to check for the functionality of .textContent or .innerText.
Is there a cross browser solution .getTheText?
If not what would be the best way around this? Is there a simple solution? (I ask given my knowledge and experience with scripting, which is limited)
** added following comments **
If this is my code block:
// build products object
var prods = [];
var brand = document.querySelectorAll('.txtStayRoomLocation');
var name = document.querySelectorAll('.txtStayRoomDescription');
var price = document.querySelectorAll('.txtStayRoomSplashPriceAmount');
for(var i = 0; i < brand.length; i++) {
//set granular vars
var prd = {};
//add to prd object
prd.brand = brand[i].innerText;
prd.name = name[i].innerText;
prd.price = price[i].innerText;
prd.quantity = window.session_context_vars.BookingContext.Booking.ReservationLineItems[i].ReservationCharges.length/2;;
//add to prods array
prods.push(prd);
}
Then if I understand the syntax from the comments and the question linked to in the comment, is this what I should do:
// build products object
var prods = [];
var brand = document.querySelectorAll('.txtStayRoomLocation');
var name = document.querySelectorAll('.txtStayRoomDescription');
var price = document.querySelectorAll('.txtStayRoomSplashPriceAmount');
for(var i = 0; i < brand.length; i++) {
//set granular vars
var prd = {};
//add to prd object
prd.brand = brand[i].textContent || brand[i].innerText;
prd.name = name[i].textContent || name[i].innerText;
prd.price = price[i].textContent || price[i].innerText;
prd.quantity = window.session_context_vars.BookingContext.Booking.ReservationLineItems[i].ReservationCharges.length/2;;
//add to prods array
prods.push(prd);
}
So using or with a double bar || assigns the first non null value?
Re: your edit, not quite. The way to access methods or properties on an object (eg a DOM element) is to use dot notation if you have the name itself, or square brackets in case of variables/expressions (also works with strings, as in obj["propName"], which is equivalent to obj.propName). You can also just test the property against one element and use that from there on:
// build products object
var prods = [];
var brand = document.querySelectorAll('.txtStayRoomLocation');
var name = document.querySelectorAll('.txtStayRoomDescription');
var price = document.querySelectorAll('.txtStayRoomSplashPriceAmount');
for(var i = 0; i < brand.length; i++) {
//set granular vars
var prd = {};
//add to prd object
var txtProp = ("innerText" in brand[i]) ? "innerText" : "textContent"; //added string quotes as per comments
prd.brand = brand[i][txtProp];
prd.name = name[i][txtProp];
prd.price = price[i][txtProp];
prd.quantity = window.session_context_vars.BookingContext.Booking.ReservationLineItems[i].ReservationCharges.length/2;;
//add to prods array
prods.push(prd);
}
Regarding the line:
var txtProp = (innerText in brand[i]) ? innerText : textContent;
The in keyword checks an object to access the property (syntax: var property in object). As for the question notation (I made an error earlier, using ||, the correct thing to use was a :),
var myVar = (prop in object) ? object[prop] : false;
As an expression, it basically evaluates the stuff before the ?, and if it's true, returns the expression before the :, else the one after. So the above is the same as / a shorthand for:
if(prop in object){
var myVar = object[prop];
}
else{
var myVar = false;
}
Since you are checking between two properties only and wanting to assign one or the other, the shortest way would indeed be:
var txtProp = brand[i].innerText || brand[i].textContent;
It would basically test the first property, and if it were false or undefined, it would use the second one. The only reason I (pedantically) avoid using this is because the first test of a || b would fail even if a existed but just had a value of 0, or an empty string (""), or was set to null.

Changing the selected object in Javascript

What I am trying to do is rewrite content on the page depending on which object I have selected. I have some objects like so:
function floorPlan(name,rev,sqft,bedrm,bthrm) {
this.name = name;
this.rev = rev;
this.sqft = sqft;
this.bedrm = bedrm;
this.bthrm = bthrm;
}
// 1BR Plans
var a1 = new floorPlan('A1',false,557,1,1);
var a2 = new floorPlan('A2',false,652,1,1);
var a3 = new floorPlan('A3',false,654,1,1);
var a4 = new floorPlan('A4',false,705,1,1);
var a5 = new floorPlan('A5',false,788,1,1);
// The Selected plan
var currentPlan = floorPlan.a1;
I am having the user control this via a .click() function in a menu:
$('.sideNav li').click(function() {
// Define the currentPlan
var current = $(this).attr('id');
var currentPlan = floorPlan.current;
});
The problem is that currentPlan keeps coming back as undefined and I have no idea why. Should I be defining currentPlan differently? I can't seem to find any resources to help me find the answer.
UPDATED:
I switched out a few parts per your suggestions:
// The Selected plan
var currentPlan = a1;
and....
// Define the currentPlan
var current = $(this).attr('id');
currentPlan = current;
However, everything is still returning undefined in the click function (not initially though).
First of all $('this') should be $(this)
Secondly you're trying to use a read ID from your LI as a variable name. That doesn't work. If you store your plans in an array you can use the ID to search in that array:
var plans=Array();
plans["a1"]=new floorPlan('A1',false,557,1,1);
plans["a2"]=new floorPlan('A2',false,652,1,1);
Then your jQuery code should be altered to this:
$('.sideNav li').click(function() {
// Define the currentPlan
var current = $(this).attr('id');
var currentPlan = plans[current];
alert(currentPlan);
});
I created a JSFiddle for this. Is this what you were looking for?
Use as floorPlan.currentPlan = a1;
instead of var currentPlan = floorPlan.a1;
Please create a plunker and will correct if any issue.
I spot two errors.
When you write var inside a function, that variable is only accessible with that function. Right now you are creating a new variable in your anonymous function that is "hiding" the global variable with the same name.
So, first remove the var keyword from the assignment in the anonymous function (the one you call on "click").
Secondly I think you mean to assign floorPlan[current].
The final line should read:
currentPlan = floorPlan[current];

How to add a variable to a javascript/jQuery object?

Well, the problem is quite simple. I got an object of parsed table rows. Code for it is this:
var erg = [];
$("tr").each(function (index) {
var row = {};
var test = $(this).children();
row['column1'] = test[0].textContent;
row['column2'] = test[1].textContent;
row['column3'] = test[2].textContent;
row['column4'] = test[3].textContent;
row['column5'] = test[4].textContent;
row['column6'] = test[5].textContent;
row['column7'] = test[6].textContent;
erg.push(row);
});
And I wanna pass a variable var my_variable="blabla" to it without ruining the structure of the object. So how could i bring that object into a structure like this?:
Object{my_variable="my_variable_value"}, Object{my_table=[Object{...}, Object{...}]} //all the objects of the table
$.extend({}, erg, my_variable); only messed my object up.
I want it in that structure so i can pass it as json to my php script and filter my variable easily. Any tips, links, code snippets? :)
I'm not sure at which point you want to add that, but you may simply wrap your array with another object, and add your property to that same object.
This is basically what Florent's answer does, but using an object literal instead of a "class" and prototype:
// (your current code)
var wrapper = {
my_variable: 'something',
my_table: erg
};
You can define a class and add the needed variables to its prototype.
First you need a little utility to do that:
function createSharedStruct() {
// Define a shared structure
var Struct = function() {};
// Define a method to define a shared variable
Struct.share = function(variable, value) {
Struct.prototype[variable] = value;
};
return Struct;
}
And then, update your code:
// Create the shared structure
var rowClass = createSharedStruct();
// Register your shared variables
rowClass.share('my_variable', 'my_variable_value');
var erg = [];
$("tr").each(function (index) {
var test = $(this).children();
// Create a new row
var row = new rowClass();
row['column1'] = test[0].textContent;
row['column2'] = test[1].textContent;
row['column3'] = test[2].textContent;
row['column4'] = test[3].textContent;
row['column5'] = test[4].textContent;
row['column6'] = test[5].textContent;
row['column7'] = test[6].textContent;
erg.push(row);
});
// No matter when you share a variable, it will be defined among
// all instances of the same struct.
rowClass.share('my_other_var', 42);
Now you can access shared variables:
console.log(erg[0].my_other_variable); // 42
console.log(erg[1].my_other_variable); // 42
Demo available on JSFiddle.

Alternative to using eval()

I've heard a lot of rumblings about how "evil" or even "misunderstood" the eval function is, so I've decided to remove it from my code. The problem is I don't know what to replace it with.
Here's a quick rundown of my current code. I have a series of arrays (just 2 for the example below) declared at the beginning, and then based on a button click one of them gets loaded into a variable that is passed into a function.
Here's some basic HTML
<div class="button" data-name="button1">Button1</div>
<div class="button" data-name="button2">Button2</div>
and the JS (with jQuery)
var butName = null;
var eArray = null;
var button1Logo = ["..path/to/pic1.png","..path/to/pic2.png"];
var button2Logo = ["..path/to/pic3.png","..path/to/pic4.png"];
$(".button").mouseup(function(){
/*give a butName*/
butName = $(this).attr("data-name");
/*give the array from the button*/
eArray = eval(butName + "Logo");
});
Doing it this way assigns the array to the variable and not just a string that says "butnameLogo" which is why I used eval. But I'm looking to get away from that.
I know I can add a new attribute to the html and just retrieve that for the variable but I don't want to add more html when I can possibly do it with JS.
I've also tried making an object with strings loaded into it as seen in this answer: https://stackoverflow.com/a/16038097/1621380 but that resulted in just a string again, and not assigning a variable.
Wondering if you smart people have any better suggestions!
Replace
var button1Logo = ["..path/to/pic1.png","..path/to/pic2.png"];
var button2Logo = ["..path/to/pic3.png","..path/to/pic4.png"];
with an object, where the keys are your button names:
var buttonLogos = {
button1: ["..path/to/pic1.png","..path/to/pic2.png"],
button2: ["..path/to/pic3.png","..path/to/pic4.png"]
};
Then instead of the eval you can simply do
eArray = buttonLogos[butName]
(or buttonLogos[butName + "Logo"] if you want to call the keys button1Logo and button2Logo, but I can't really see the point now that they are nicely contained within a buttonLogos object)
Use an object:
var butName = null;
var buttonsLogos = {
button1: ["..path/to/pic1.png", "..path/to/pic2.png"],
button2: ["..path/to/pic3.png", "..path/to/pic4.png"]
};
$(".button").mouseup(function(){
/*give a butName*/
butName = $(this).attr("data-name");
/*give the array from the button*/
eArray = buttonsLogos[butName];
});
Consider making the data available as properties of an object, then you can control access to the object through scope and only need one (global?) variable for all such data.
If global scope is needed, then:
var dataObj = {
button1Logo: ["..path/to/pic1.png","..path/to/pic2.png"],
button2Logo: ["..path/to/pic3.png","..path/to/pic4.png"]
}
and later:
var eArray = dataObj[this.data-name + 'Logo'];
You may want to call the data object something more meaningful than dataObj though.
The best option is to define an object which holds all our button paths:
var buttons = {
"1": ["..path/to/pic1.png", "..path/to/pic2.png"],
"2": ["..path/to/pic3.png", "..path/to/pic4.png"]
};
$(".button").mouseup(function(){
/* give a butName */
var butName = $(this).attr("data-name");
/* give the array from the button */
var eArray = buttons[butName];
});
If your variables reside in the global scope, you could use the bracket notation to access them:
eArray = window[butName + "Logo"];
Note that this solution is not recommended. The first code sample is much cleaner and more maintainable.
Imagine a situation where you would have to move all the code into a 'deeper' context (!= global context). Nothing would work anymore.
You can do this very nicely with arrays and array indexes. You needn't find and use variable names at all. Even your data- attributes are unnecessary.
var eArray;
var buttonLogos = [
["..path/to/pic1.png","..path/to/pic2.png"],
["..path/to/pic3.png","..path/to/pic4.png"]
];
var buttons = $(".button").mouseup(function(){
var idx = buttons.index(this);
eArray = buttonLogos[idx];
});
The key line in this is buttons.index(this). This method call gets the position of the current element among all the elements matched by $(".button"). We then use this index to select the relevant element from the buttonLogos array.
You're taking a very circuitous route by using eval here.
You'd be much better off doing something like this:
var paths = {
button1: ["..path/to/pic1.png","..path/to/pic2.png"],
button2: ["..path/to/pic3.png","..path/to/pic4.png"]
};
$(".button").mouseup(function(){
/*give the array from the button*/
eArray = paths[$(this).attr("data-name")];
});
eval should only be used if you need to execute code (usually from a 3rd party source), and even that is rare. If you ever find yourself saying "i should use eval here", there's almost definitely a better alternative, and you should try and find it.

Object within object add property with JavaScript

I have an object within an object. It looks like this.
var myLib = {
object1: {}
}
My basic problem is that I wanted to end up like this. So I would like to do this dynamically I will not know the property's or additional objects until run time.
var myLib = {
object1: ({"A1":({"Color":"Blue",
"height":50})
})
}
From reading here on Stack Overflow I know that I can create an object within an object by simply going like this:
myLib.Object1["A1"] = "Something"
But this does not produce what I'm looking for.
I tried this syntax which I know is wrong but basically
mylib.Object1["A1"].["color"]="Blue";
so basically here is the question. I would like to create object "A1" under "mylib.Object" and immediately add property color = "blue" to "A1". I would need to do this for several other properties, but if I can figure out how to do this for one, I can figure it out for the rest. How can I accomplish this task?
No jQuery, please. Just plain old JavaScript is what I'm looking for.**
Once I create the object and properties I would imagine I can just use a for loop to loop through the properties for that object. Like so:
for(key in mylib.Object1["A1"]){}
Right?
You can create it all from scratch like this:
var myLib = {};
myLib.object1 = {};
// assuming you get this value from your code somewhere
var x = "A1";
myLib.object1[x] = {Color: "Blue", height: 50};
Or, if all values are in variables:
var myLib = {};
myLib.object1 = {};
// assuming you get this value from your code somewhere
var x = "A1";
var colorProp = "Color";
var colorPropValue = "Blue";
var heightProp = "height";
var heightPropValue = 50;
myLib.object1[x] = {}; // create empty object so we can then add properties to it
myLib.object1[x][colorProp] = colorPropValue; // add one property
myLib.object1[x][heightProp] = heightPropValue; // add another property
These syntaxes create identical results:
myLib.object1.A1 = {};
var x = "A1";
myLib.object1[x] = {};
The first can only be used when the property name is known when you write the code and when the property name follows the proper rules for a javascript identifier. The second can be used any time, but is typically used when the property name is in a variable or when it doesn't follow the rules for a javascript identifier (like it starts with a digit).

Categories