Passing variables through addEventListener - javascript

I am trying to add event to all inputs, get the value (number) from those and insertHtml them into span elements.
This is my javascript code. I have no idea how to pass the variables.
var input_selector = document.querySelectorAll('.coins_n'); // input number elements
var price_selector = document.querySelectorAll('.price'); // span elements
for(var i = 0; i <= input_selector.length; i++) {
var input = input_selector[i];
var price = price_selector[i];
input.addEventListener('input', function(){
console.log(price); // not working
console.log(input); // not working
price.innerHTML = input.value; // not working
})
}

The problem here has to do with scoping of your variables. var is a weird one, whose scope isn't really limited to the block, but to the containing function. The following two are (essentially) the same:
var input;
var i = 0;
for(; i < input_selector.length; i++) input = input_selector[i];
and
for(var i = 0; i < input_selector.length; i++) var input = input_selector[i];
both create a variable named input in global scope, and then update that variable. That means that any functions that wants to read input later will just read the last version of input, and not the version at the time you defined the handler you would trigger later.
let, however, is block scoped, and your for loop is a block. So defining let input inside the for loop will mean that input is defined uniquely for every iteration of the loop, since every time the block gets executed a new scope is created for everything in there.
The same is true for var i = 0 in your for loop - any handler that calls it later will just log the last global value of i, but if you use let, that's not the case and every iteration of the loop has its own i. So your code could simply be reduced to this:
const input_selector = document.querySelectorAll('.coins_n');
const price_selector = document.querySelectorAll('.price');
for( let i = 0; i < input_selector.length; i++ ){
input_selector[i].addEventListener('input', event => {
price_selector[i].innerHTML = event.target.value;
});
}
This is pretty complex to explain once you start typing it out, so better read what other have already written at something like MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let

Ok. I've managed it by using let.
var input_selector = document.querySelectorAll('.coins_n');
var price_selector = document.querySelectorAll('.price');
for(var i = 0; i < input_selector.length; i++) {
let input = input_selector[i];
let price = price_selector[i];
input_selector[i].addEventListener('input', function(){
price.innerHTML = this.value;
})
}

Related

How to keep changing the the reference name by loop which is pointing to the object in javascript? [duplicate]

I'm working on an ajax google maps script and I need to create dynamic variable names in a for loop.
for (var i = 0; i < coords.length; ++i) {
var marker+i = "some stuff";
}
What I want to get is: marker0, marker1, marker2 and so on.
and I guess there is something wrong with marker+i
Firebug gives me this: missing ; before statement
Use an array for this.
var markers = [];
for (var i = 0; i < coords.length; ++i) {
markers[i] = "some stuff";
}
I agree it is generally preferable to use an Array for this.
However, this can also be accomplished in JavaScript by simply adding properties to the current scope (the global scope, if top-level code; the function scope, if within a function) by simply using this – which always refers to the current scope.
for (var i = 0; i < coords.length; ++i) {
    this["marker"+i] = "some stuff";
}
You can later retrieve the stored values (if you are within the same scope as when they were set):
var foo = this.marker0;
console.log(foo); // "some stuff"
This slightly odd feature of JavaScript is rarely used (with good reason), but in certain situations it can be useful.
Try this
window['marker'+i] = "some stuff";
In regards to iterative variable names, I like making dynamic variables using Template literals. Every Tom, Dick, and Harry uses the array-style, which is fine. Until you're working with arrays and dynamic variables, oh boy! Eye-bleed overload. Since Template literals have limited support right now, eval() is even another option.
v0 = "Variable Naught";
v1 = "Variable One";
for(i = 0; i < 2; i++)
{//console.log(i) equivalent is console.log(`${i}`)
dyV = eval(`v${i}`);
console.log(`v${i}`); /* => v0; v1; */
console.log(dyV); /* => Variable Naught; Variable One; */
}
When I was hacking my way through the APIs I made this little looping snippet to see behavior depending on what was done with the Template literals compared to say, Ruby. I liked Ruby's behavior more; needing to use eval() to get the value is kind of lame when you're used to getting it automatically.
_0 = "My first variable"; //Primitive
_1 = {"key_0":"value_0"}; //Object
_2 = [{"key":"value"}] //Array of Object(s)
for (i = 0; i < 3; i++)
{
console.log(`_${i}`); /* var
* => _0 _1 _2 */
console.log(`"_${i}"`); /* var name in string
* => "_0" "_1" "_2" */
console.log(`_${i}` + `_${i}`); /* concat var with var
* => _0_0 _1_1 _2_2 */
console.log(eval(`_${i}`)); /* eval(var)
* => My first variable
Object {key_0: "value_0"}
[Object] */
}
You can use eval() method to declare dynamic variables. But better to use an Array.
for (var i = 0; i < coords.length; ++i) {
var str ="marker"+ i+" = undefined";
eval(str);
}
In this dynamicVar, I am creating dynamic variable "ele[i]" in which I will put value/elements of "arr" according to index. ele is blank at initial stage, so we will copy the elements of "arr" in array "ele".
function dynamicVar(){
var arr = ['a','b','c'];
var ele = [];
for (var i = 0; i < arr.length; ++i) {
ele[i] = arr[i];
] console.log(ele[i]);
}
}
dynamicVar();
var marker = [];
for ( var i = 0; i < 6; i++) {
marker[i]='Hello'+i;
}
console.log(marker);
alert(marker);
var marker+i = "some stuff";
coudl be interpreted like this:
create a variable named marker (undefined); then add to i; then try to assign a value to to the result of an expression, not possible.
What firebug is saying is this:
var marker; i = 'some stuff';
this is what firebug expects a comma after marker and before i;
var is a statement and don't (apparently) accepts expressions.
Not so good an explanation but i hope it helps.

React onClick Events in loops

Im trying to bind some variables to a onClick event inside a React Component. Im doing this from within a for loop because I need many of them.
My code looks like this:
generateCells(pieData){
var cells = []
for(var j = 0; j < pieData.length; j++){
var imageIndex = pieData[j].image;
var image = [pieData[j].innerIndex, pieData[j].angle];
console.log(image);
var rowLength = pieData.length;
cells.push(<Cell key={imageIndex} onClick={() => this.retake(image, rowLength)} fill={this.state.progressState[imageIndex] ? '#00C49F' : '#FF8042'}/>)
}
return cells;
}
But my image variable in the onClick event is always from the last iteration of the loop. How is this possible?
Thanks for any advice!
This is because you're using var and it's creating a variable scoped into you're generateCells() function. Try with let to see the difference (the variable scope will be the for loop)
I did a Codepen some time ago to view the difference between them, this may help you: https://codepen.io/ArthyFiciel/pen/rxKeQR
Change var to let in for loop. let create new binding and let creates a variable declaration for each loop which is a block-level declaration. while var scoped to the global scope so by the time you click var value is attached to the global scope and displaying last value:
for(let j = 0; j < pieData.length; j++){
var imageIndex = pieData[j].image;
var image = [pieData[j].innerIndex, pieData[j].angle];
console.log(image);
var rowLength = pieData.length;
cells.push(<Cell key={imageIndex} onClick={() => this.retake(image, rowLength)} fill={this.state.progressState[imageIndex] ? '#00C49F' : '#FF8042'}/>)
}
return cells;
Demo : https://stackblitz.com/edit/react-geu71r
Change var to let infor loop, this should help

createElement creates infinite loop

I'm really new to javascript, and coding in general, and I can't understand why this causes an infinite loop:
let newTr = document.createElement('tr');
If I take it out, the webpage loads fine, but if I leave it in, the webpage never fully loads and my browser uses 50% of my CPU.
Here's the rest of my code:
// client-side js
// run by the browser each time your view template referencing it is loaded
console.log('hello world :o');
let arrPfcCases = [];
// define variables that reference elements on our page
const tablePfcCases = document.getElementById("tablePfcCases");
const formNewPfcCase = document.forms[0];
const caseTitle = formNewPfcCase.elements['caseTitle'];
const caseMOI = formNewPfcCase.elements['caseMOI'];
const caseInjuries = formNewPfcCase.elements['caseInjuries'];
// a helper function to call when our request for case is done
const getPfcCaseListener = function() {
// parse our response to convert to JSON
arrPfcCases = JSON.parse(this.responseText);
// iterate through every case and add it to our page
for (var i = 0; i = arrPfcCases.length-1;i++) {
appendNewCase(arrPfcCases[i]);
};
}
// request the dreams from our app's sqlite database
const pfcCaseRequest = new XMLHttpRequest();
pfcCaseRequest.onload = getPfcCaseListener;
pfcCaseRequest.open('get', '/getDreams');
pfcCaseRequest.send();
// a helper function that creates a list item for a given dream
const appendNewCase = function(pfcCase) {
if (pfcCase != null) {
tablePfcCases.insertRow();
let newTr = document.createElement('tr');
for (var i = 0; i = pfcCase.length - 1; i++) {
let newTd = document.createElement('td');
let newText = document.createTextNode(i.value);
console.log(i.value);
newTd.appendChild(newText);
newTr.appendChild(newTd);
}
tablePfcCases.appendChild(newTr);
}
}
// listen for the form to be submitted and add a new dream when it is
formNewPfcCase.onsubmit = function(event) {
// stop our form submission from refreshing the page
event.preventDefault();
let newPfcCase = [caseTitle, caseMOI, caseInjuries];
// get dream value and add it to the list
arrPfcCases.push(newPfcCase);
appendNewCase(newPfcCase);
// reset form
formNewPfcCase.reset;
};
Thanks!
P.S. There are probably a ton of other things wrong with the code, I just can't do anything else until I figure this out!
As an explanation, in your code
i = pfcCase.length - 1
assigned the value of pfcCase.length - 1 to i. The syntax of that part of the loop should be
an expression to be evaluated before each loop iteration. If this expression evaluates to true, statement is executed.
The evaluation of your code made no sense.
Evaluating
i < pfCase.length
before each iteration to check that the current index is less than the length of the array, however, works correctly.
Here is no conditional statement here. In this statement you are assigning pfcCase length minus 1 to the I variable.
for (var i = 0; i = pfcCase.length - 1; i++) {
You have to compare the i variable to the length of the pfcCase minus 1.
This should work.
for (var i = 0; i < pfcCase.length - 1; i++) {
noticed something else
This line does not do what you think it dose.
let newText = document.createTextNode(i.value);
i is just the index i.e. a number. It does not have the value property.
This is what you are looking to do.
let newText = document.createTextNode(pfcCase[i].value);
my preference (forEach)
I prefer using the array forEach method. It’s cleaner and less prone to mistakes.
pfcCase.forEach( function(val){
let newTd = document.createElement('td');
let newText = document.createTextNode(val.value);
console.log('The array element is. '. val.value, ' The value is. ', val.value);
newTd.appendChild(newText);
newTr.appendChild(newTd);
});

Making variables assigned HTML ids with a function

I can make variables one by one like this:
var bookName = document.getElementById('bookName').value,
author = document.getElementById('author').value,
translator = document.getElementById('translator').value,
pageCount = document.getElementById('pageCount').value,
publisher = document.getElementById('publisher').value,
isbn = document.getElementById('isbn').value,
printingYear = document.getElementById('printingYear').value;
But it's so hard to write and it doesn't fit with the DRY rule. So I changed the code to this:
function variableMaker(argument) {
var tags = document.getElementsByTagName(argument);
for (var i = 0; i < tags.length; i++) {
var tags[i].name = tags[i].value;
}
}
variableMaker(input);
But I can't understand if it is the true way or if it is working? How do I check if it's true or not?
In this code, I tried to get the computer find all the input tags and make variables with their name property and assign it to its values for each of them.
If I understand correctly then you want to gather data from all <input> elements. If so, then you need to call it like this:
variableMaker('input'); // use quotes!
Still even then your function does not return anything, it just ends.
You'd also better create your own object for the return collection, instead of adding values to an existing object.
Here is a working solution:
function variableMaker(tagName) {
var elements = document.getElementsByTagName(tagName);
var items = {};
for (var i = 0; i < elements.length; i++) {
var elem = elements[i];
items[elem.id] = elem.value; // Use id as key, each points to the corresponding value.
}
return items;
}
var values = variableMaker('input');
console.log(values); // show the entire return object
console.log(values.author); // access individual items from the return object
console.log(values.isbn);
<input type="text" id="author" value="Dahl">
<input type="text" id="isbn" value="1234">
.

Is there a way to loop this?

Is there a way to loop a declaration of a variable? just a loop to help me declare the variables so i dont have to do the monotonous work of change the numbers of the variable
var height1 = document.getElementById('height1').value;
var height2 = document.getElementById('height2').value;
var height3 = document.getElementById('height3').value;
var height4 = document.getElementById('height4').value;
var height5 = document.getElementById('height5').value;
var height6 = document.getElementById('height6').value;
var height7 = document.getElementById('height7').value;
var height8 = document.getElementById('height8').value;
var height9 = document.getElementById('height9').value;
var height10 = document.getElementById('height10').value;
var height11 = document.getElementById('height11').value;
var height12 = document.getElementById('height12').value;
var height13 = document.getElementById('height13').value;
var height14 = document.getElementById('height14').value;
var height15 = document.getElementById('height15').value;
var height16 = document.getElementById('height16').value;
This is not a right way of coding that, Just do like,
var heights = [];
Array.from(document.querySelectorAll("input[id^=height]")).forEach(function(itm){
heights.push(itm.value);
});
And now you can iterate the array heights to manipulate the values as per your requirement.
The logic behind the code is, querySelectorAll("input[id^=height]") will select the input elements that has id starts with the text height. Since the return value of querySelectorAll is a nodelist, we have to convert it as an array before using array functions over it. So we are using Array.from(nodelist). That will yield an array for us. After that we are iterating over the returned array by using forEach and pushing all element's value into the array heights.
This is almost always an indication that you want an array. Something like this:
var heights = [];
for (var i = 1; i <= 16; i++) {
heights.push(document.getElementById('height' + i).value);
}
Then you can reference a value from the array with something like:
heights[1]
Though technically since in JavaScript your window-level variables are indexable properties of the window object, you can essentially do the same thing with variable names themselves:
for (var i = 1; i <= 16; i++) {
window['height' + i] = document.getElementById('height' + i).value;
}
Then you can still use your original variables:
height1
Though in the interest of keeping things outside of window/global scope, maintaining the array seems a bit cleaner (and semantically more sensible).
This seems to be a good use case for an object:
var heights = {};
for (var i = 1; i <= 16; i++) {
heights[i] = document.getElementById('height' + i).value;
}
Maybe its time to introduce function:
Generally speaking, a function is a "subprogram" that can be called by code external (or internal in the case of recursion) to the function. Like the program itself, a function is composed of a sequence of statements called the function body. Values can be passed to a function, and the function will return a value.
function getHeight(id) {
return document.getElementById(id).value;
}
Call with the wanted id and use it like a variable.
getHeight('height1')
Normally you would put them in an array.
var heights = []
for (i = 1; i < 17; i++) {
heights[i] = document.getElementById('height' + i).value;;
}
Beware this will give you a hole at the start of the array ie heights[0] has nothing in it. If you use this to iterate it won't matter...
for (var i in heights) {
alert(heights[i]);
}

Categories

Resources