Cannot use variables to change css property - javascript

I am trying to change the background-color of an element by clicking on a button. In 2 implementations of javascript, I am storing slightly different values in the same variable, and using the variables to change the background-color. I expect both implementations to work, but only one works. Here are the details.
In HTML, I have a <div id="foo">Lorem</div> element and a <button id="button">Click</button> element.
I have 2 different codes for JavaScript. The first one is:
var button = document.getElementById("button");
var x = document.getElementById("foo").style;
button.addEventListener("click",function(){
x.backgroundColor="red";
});
The second one is:
var button = document.getElementById("button");
var x = document.getElementById("foo").style.backgroundColor;
button.addEventListener("click",function(){
x="red";
});
The first one works, but the second one does not. The only difference between the two code snippets is in the first one, the variable x did not include backgroundColor and the background color of x was changed using x.backgroundColor="red";. In the second one, the variable x did include backgroundColor and the color was changed(tried to change) using x="red";.
Why does the first way work, but the second way does not?

In the first example, you are pointing x at the style object that exists for the foo component. When you assign a string to x.backgroundColor, you are effectively assigning a string to document.getElementById("foo").style.backgroundColor since x and style point to the same object in memory.
In the second example, you are simply assigning the style.backgroundColor string to x. You then assign a new string to x.

All that second attempt does is reassign x away from the current backgroundColor property value to the value of red. It's no different than this:
var x = 10;
x = 20;
console.log(x); // 20
In other words, x is not a reference to the backgroundColor property, it's a reference to the value stored in that property, so let's say the original value of the property was "Yellow", then x would be storing the string Yellow, it's not a reference to where that value came from - - only the value itself. So, when you change it, you aren't doing anything to the backgroundColor property, you are just changing the value of x from "Yellow" to "Red".
You can store the value you'd like to assign to backgroundColor as shown below:
// x doesn't store a reference to the object property. It stores
// the VALUE of the property at this moment in time.
var x = document.getElementById("foo").style.backgroundColor;
var color = "green";
document.getElementById("button").addEventListener("click", function(){
console.log("Original color of element: " + x);
x = "red"; // All this does is make x point away from it's previous value
console.log("New value of 'x': " + x);
// You need a statement that assigns a new value to the property
document.getElementById("foo").style.backgroundColor = color;
console.log("New color of element: " + document.getElementById("foo").style.backgroundColor);
});
<div id="foo" style="background-color:orange;">Lorem</div>
<button id="button">Click</button>

var x = document.getElementById("foo").style;
in this example, x is a style object, like:
{
padding: 0;
backgroundColor: blue;
}
while in the second example, x is actually a string, and in the callback function, x only is assigned with new string.
you can console.log(x) to see the differences of these two "x"s.

Related

How to read a dynamic string as an object reference

I need to set a button's value as a dynamic string. This string should be turned into an object reference. When the button is clicked the object will be loaded into a new variable. As of right now, it is reading in the string and not reading it as a reference.
Object:
var Wall ={
"Option1":{},
"Option2":{},
"Option3":{
"Option1_3":{
Option1_1_3:{
aTotal:100,
Total_Something_Else:20,
Another_Total:40,
More_totals:20,
Total:20,
},
"Option2_1_3":{},
"Option3_1_3":{}
},
"Option2_3":{},
},
"Option4":{},
"Option5":{}};
Code to create the button:
var options = ['1','2','3','4']
for (var a = 1; a < 2; a++) {
for (var b = 3; b <4; b++) {
for (var i = 0; i < 1; i++) {
document.getElementById('Option1_3').innerHTML = (`<button class="button" value='${JSON.stringify('Wall.Option'+options[i]+'.Option'+a+'_'+b)}' onclick= "PopulateGraph(this.value)">Wall Time</button>`);
}
}
}
function PopulateGraph(val){
console.log(Wall.Option3.Option1_3); //The ouptut of this is what I want
console.log(JSON.parse(val));
}
The above code needs to behave like this piece of code:
Code that works the way I need it to
Output: The top output is what I would need:
Output
Please be mindful that I am very new to coding and javascript. If you have any suggestions at all to make my code better in the long run or have references you think will be helpful please do not be shy. I apologize if this seems like a no-brainer. I have tried a few different solutions to my issue (using eval() and scope[] )and I have yet to figure out a solution. I am giving a very simplified version of my code but the issue is the same.
Use the bracket notation [] to access the properties.
JSON.stringify(Wall['Option'+options[i]]['Option'+a+'_'+b])
Additionally i would create a variable with the text to avoid doing all that work in the same line
for (var i = 0; i < 1; i++) {
const optionValue = Wall['Option' + options[i]]['Option' + a + '_' + b];
const optionJson = JSON.stringify(optionValue);
document.getElementById('Option1_3').innerHTML = (`<button class="button" value='${optionJson}' onclick= "PopulateGraph(this.value)">Wall Time</button>`);
}
At least these issues:
The reference you try to build with i, a and b does not exist in your object. Your object has only a nested object for "Option3", but as i is initialised in the for loop as 0, you are not targeting that property.
The argument passed to JSON.stringify should be the nested value in the object, but you are passing a string, and so that string will be stringified (to yet another string).
I understand you have used single quotes to delimit the value of the value attribute, but (if the previous points are fixed) the targetted value within the Wall object could be a string having a single quote, and then your HTML will still be broken, as that will end the value attribute which you had delimited with single quotes.
As the Wall object is dynamically populated, you risk to be building the buttons too soon -- before the Wall object is populated -- and so the value attributes might still be wrong even when the above issues are resolved.
These complexities can be avoided by not building your button as a HTML string, but using the DOM methods to create the button element together with its attributes.
Furthermore, it seems that the information you store in the value property can be derived dynamically without that attribute:
We can find the parent element of the clicked button, get it's id attribute, and then we know the second-level property in the Wall object.
The Wall object seems to be structured in a way that the first-level property name can be derived from a second-level property name.
Also, you can iterate the properties in the Wall object without the need of a or b integers.
Here is how it could be done:
var Wall ={
"Option1":{},
"Option2":{},
"Option3":{
"Option1_3":{
"Option1_1_3":{
aTotal:100,
Total_Something_Else:20,
Another_Total:40,
More_totals:20,
Total:20,
},
"Option2_1_3":{},
"Option3_1_3":{}
},
"Option2_3": {
"Option2_1_3":{
test:1
}
},
},
"Option4":{},
"Option5":{}
};
for (let option of Object.values(Wall)) {
for (let id in option) {
let container = document.getElementById(id);
if (container) {
let button = document.createElement("button");
button.className = "button";
button.textContent = "Wall Time for " + id;
button.addEventListener("click", PopulateGraph);
container.appendChild(button);
}
}
}
function PopulateGraph() { // no argument.
// Drill down in the Wall object, based on the id of the button's container
let val = this.parentNode.id.match(/\d+/g).reduceRight(
(acc, _, i, arr) => acc[`Option${arr.slice(i).join("_")}`],
Wall
);
console.log(val);
}
<div id="Option1_3"></div>
<div id="Option2_3"></div>

iterating through and changing nodes in singly linked list

The problem is I don't understand why this code works. It works, but I just can't wrap my mind around it. Here's a function that deletes a node from a singly linked list. I feel like it shouldn't work because it's not actually changing any of the elements in the list, I'm just changing the value of a variable I have set equal to something in the list. In other words, when I create a "runner" variable to iterate through the list, why are the changes I make to "runner" actually changing the list itself. Analogously, if I do
var x = 1
var y = x
y = 2
Obviously, x is still going to equal 1. Why isn't the same true for my Linked List "runner". In the deleteNode function below, why does changing the runner.next value actually change anything in the node or list that exists outside of the function?
function deleteNode(head, position) {
var runner = head
var counter = 0
while (runner) {
if (counter == position - 1) {
runner.next = runner.next.next
return head;
}
runner = runner.next
counter++
}
}
Its because runner is an object, and so the runner variable is a reference to that object.
for example
const x = {a:1}
const y = x;
x.a = 3
console.log(y.a) // this will print 3 also

How to increment a variable after button press

I was wondering how I could increase a variable when a button is clicked. here is my code. Can somebody point me in the right direction? thanks.
I am thinking when the button1 is clicked it will increase team 1's score by like 10, and will decrease if it is clicked again if that is necessary.
var Team2;
var Team2 == 0;
var Team1;
var Team1 == 0;
document.getElementById("calc").innerHTML = Team1;
function clicked(button1)
{
var team1 = team1 + 45
}
</SCRIPT>
<p>
Players for Team 1
First, the variable part: suppose you define a variable var value = 0. To increase it by 10, you can write value = value + 10, but in JavaScript this can be shorten to:
value += 10
The same way, to decrease it, just write value -= 10.
To call the function, you write onClick="someFunction()" (not the best practice), and then you define the function:
function someFunction(){
value += 10
};
This is the working fiddle: https://jsfiddle.net/gerardofurtado/6qh1yhsj/
If you want to see how to do the same thing without the onClick="someFunction()" part, here is a fiddle: https://jsfiddle.net/gerardofurtado/wff8mph3/
PS: I see that in your code you wrote var team2 == 0. In JavaScript, two equal signs make a comparison operator: http://www.w3schools.com/js/js_comparisons.asp
PS2: In JavaScript, you have to mind the scope. Once you defined the var team1, you can change it inside the function just by writing team1. But if you do as you did:
function someFunction(){
var team1 = team1 + 45
};
This team1 is not the same previous variable team1 defined outside the function. It's a different variable. And, as the team1 to the right of the equal sign is not defined, this will return a NaN (Not-A-Number).

Variable not changing when function is called

Anyone see a bug in my code that stops the variable "player1Bananas" from changing? Everything else in the function works fine.
//update playerStats arguments:
//banana is a banana object
//x and y are the banana's position
//players bananas is player1Bananas or player2Bananas,
//ballPosition is "left" or "right"
updatePlayerStats: function(banana, x, y, playersBananas, ballPosition) {
playersBananas += 1;
gameController.bananasTaken += 1;
banana.x = x;
banana.y = y;
gameController.player1Score = 0;
gameController.player2Score = 0;
gameController.setUpPaddles();
gameController.setUpBall(ballPosition);
},
gameController.updatePlayerStats( gameController.Banana1, 20, gameController.canvas.height - 20 - gameController.Banana1.height,
gameController.player1Bananas, "left");
Thanks!
You're passing the value of gameController.player1bananas as a parameter to a function.
In the function that value is assigned to local variable playersBananas and is restricted by scope. When you make a change to it, you're no longer making a change to that variable you originally passed but instead the new variable playersBananas.
Example: http://jsfiddle.net/GHkJ6/
To fix this, you need to pass it as an object. JavaScript won't pass it as a value, but instead the object itself.
Example: http://jsfiddle.net/F446Q/
Because in JavaScript numbers are passed by value...
You have to pass something else than a single number, like a player state object({id:1, bananas:17} for example).
gameController.player1Bananas is a primitive type, so it is passed by value... not reference. This means that inside the function playerBananas no longer has any reference to player1Bananas, it is simply an integer value.
Try passing in an object instead... for example, you could have a player object with a bananaCount property. Then pass the player object into the function and increment the bananaCount property of the player object.
eg.
//update playerStats arguments:
//banana is a banana object
//x and y are the banana's position
//players bananas is player1Bananas or player2Bananas,
//ballPosition is "left" or "right"
updatePlayerStats: function(banana, x, y, player, ballPosition) {
player.bananaCount += 1;
gameController.bananasTaken += 1;
banana.x = x;
banana.y = y;
gameController.player1Score = 0;
gameController.player2Score = 0;
gameController.setUpPaddles();
gameController.setUpBall(ballPosition);
},
gameController.updatePlayerStats( gameController.Banana1, 20, gameController.canvas.height - 20 - gameController.Banana1.height,
gameController.player1, "left");
See this link for a good explanation.. http://snook.ca/archives/javascript/javascript_pass
change player1Bananas to global variable then change that same player1Bananas inside the function

Can I change a javascript variable within another variable?

Here is my problem. After hitting the increment button, y is equal to 7, but z stays equal to 7 instead of changing to 8. Why don't javascript variables update within other variables? Is there a solution to this problem? I'm sure there is a fundamental property of javascript I am failing to understand, but I can't seem to figure it out.
<body>
<script>
var y=6;
var z=1+y;
</script>
<button onclick="y++">Increment</button>
<button onclick="alert(y)">What is y?</button>
<button onclick="alert(z)">What is z?</button>
</body>
The value held on variable z is calculated the moment you assign it to the variable:
var z=1+y;
If you want it to change, you have to update it manually:
<button onclick="z=++y+1">Increment</button>
Most computer languages behave like that, I believe.
In JavaScript and other languages you can use "getters" and "setters" to achieve what you want, but the code will become more complex. Judge if you really think it's necessary:
<body>
<script>
var vals = {
_y : 0,
z : 0,
set y(val) {
this._y = val;
},
get y() {
this.z = this._y + 1
return this._y;
}
}
vals.y = 6;
vals.z = 1 + vals.y;
</script>
<button onclick="vals.y++">Increment</button>
<button onclick="alert(vals.y)">What is y?</button>
<button onclick="alert(vals.z)">What is z?</button>
</body>
http://jsbin.com/udediy/1/edit
Another simpler solution is to just use a function. That would work well for the example you gave:
<body>
<script>
var y=6;
var z = function() {
return 1+y;
}
</script>
<button onclick="y++">Increment</button>
<button onclick="alert(y)">What is y?</button>
<button onclick="alert(z())">What is z?</button>
</body>
http://jsbin.com/ojorax/1/edit
y is evaluated when you set z. At that time y is 6 so z is 7. When you increase y then z is not re evaluated (as you've found out).
y=1;
z=++y;//increment y and assign the value to z
// z will be 2;
z=y++;//set z to the value of y and then increment y
// z is now 2 and y is 3
If you want one variable to be dependent on the value of another variable you can't just assgin new values to them. You have to use getters and setters functions:
var myObj={
y:0,
x:0,
setY:function (value){
this.y=value;
this.x=value+1;
},
setX:function (value){
this.x=value;
this.y=value-1;
}
}
myObj.setY(4);
console.log(myObj.x);//=5
myObj.y=2;//breaks setting x, you have to use setters
console.log(myObj.x);//=5
As you can see the line myObj.y=2 breaks setting z so you can't assign values to either myObj.y or myObj.z without breaking it.
To prevent this from happening you have to make x and y private. In JavaScript you can simulate private variables using closures.
A warning for the folling code: if you plan to create multiple instances of your object use constructor functions and have private variables using code conventions (like _privateVar) instead of real privateness since JS doesn't support it unless you're planning on not using prototype.
var myObj=(function(){
var x=0;// x and y only exist in the funciton body
var y=0;// you cannot access them in myObj.x or .y
return{
setX:function(value){
//maybe check if value is a number here
x=value;
y=value-1;
},
setY:function(value){
//maybe check if value is a number here
y=value;
x=value+1;
},
getX:function(){
return x;
},
getY:function(){
return y;
}
}
})();
myObj.setX(6);
console.log(myObj.getY());//logs 5
myObj.y=22;
console.log(myObj.getY());//still logs 5
Using bfavaretto's syntax with get and set you can assign new values but internally JavaScript will use the getters and setters functions. This does not work in older browsers like IE8 and under.
var myObj={
_y:0,
_x:0,
set y(value){
this._y=value;
this._x=value+1;
},
get y(){
return this._y;
},
set x(value){
this._x=value;
this._y=value-1;
},
get x(){
return this._x
}
}
myObj.x=4;
console.log(myObj.y);//=3
once you assign the value of z = 1 + y z references a different location and there is no further relation between z and y
One way to do it is to wrap the variable in an object, which is passed by reference rather than by value:
var obj = { y : 6 };
var obj_2 = obj;
alert(obj.y); //6
alert(obj_2.y); // 6
obj.y++;
alert(obj.y); // 7
alert(obj_2.y); // 7`

Categories