Undefined is not an object in Dropdown - javascript

I want my code to show the menu by adding a slactive class and change the value of an input from ddown collection. I have some code, which isn't working as console says that on line 9 nor ddown[i], nor slitems[j] are objects, as they're undefined. How to fix this?
var slitems = document.getElementsByClassName('slitem');
ddown = document.getElementsByClassName('ddown');
for(i=0; i<ddown.length; i++) {
ddown[i].addEventListener('click', function(){document.getElementById('sl'+i).classList.add('slactive');valueChange()});
}
function valueChange(){
for(j=0;j<slitems.length;j++){
slitems[j].addEventListener('click', function(){
ddown[i].value = slitems[j].value;
document.getElementById('sl'+i).classList.remove('slactive');
});
}
}
P.S. slitems is a collection of menu elements.

Look, what you are doing has at least two flaws:
1st: when doing this: for(i=0; i < ddown.length; i++) ... you are declaring a global variable named i that, at the end of loop will have the value ddown.length; so, in valueChange, it will always have the same value
2nd: i is set to ddown.length, that is a position that doesn´t exists in the array, hence the error you got.
To fix this, set i as a local variable using var, and pass it as an argument:
var slitems = document.getElementsByClassName('slitem');
ddown = document.getElementsByClassName('ddown');
for(var i=0; i<ddown.length; i++) {
ddown[i].setAttribute("data-index", i);
ddown[i].addEventListener('click', function(e){
var i = e.target.dataset.index;
document.getElementById('sl'+i).classList.add('slactive');valueChange(i)
});
}
function valueChange(i){
for(var j=0;j<slitems.length;j++){
slitems[j].setAttribute("data-index", j);
slitems[j].setAttribute("data-index2", i);
slitems[j].addEventListener('click', function(e){
var j = e.target.dataset.index;
var i = e.target.dataset.index2;
ddown[i].value = slitems[j].value;
document.getElementById('sl'+i).classList.remove('slactive');
});
}
}
EDIT
Changed the code to add the variables used in iterators as node attributes, what should fix the variable scope issue.

Related

How can I loop over element ids in an array and assign them to variables?

I'm trying to refactor my window.onload function so as to avoid redundancy. I'd like to loop over the elements I'm assigning to global variables, using their ids. Initially, I was able to assign onclick functions with a loop, but now I'm not able to reproduce this in a fiddle. But the main issue is simply trying to do this (see fiddle):
var gragh, gorgh;
var ids = ["gragh", "gorgh"];
for (var i = 0; i < ids.length; i++) {
ids[i] = document.getElementById(ids[i]);
// TypeError: document.getElementById(ids[i]).onclick = doStuff;
}
//console.log(gragh); undefined
This is supposed to assign the variables gragh and gorgh to p elements which have the same ids. Within the loop, ids[i] seems to refer to the p elements. After the loop, however, these variables are undefined. This also doesn't work when looping through an array with these variables not surrounded by quotes. I've even tried using eval(), with mixed results. So my question is, how can I get this to work? And also, why doesn't this work? If ids = [gragh, gorgh] (without the quotes), what do these variables within the array refer to?
Don't reassign it in your loop, try using a new array to populate. Think of it as a reference - you're modifying it while looping.
var gragh, gorgh;
var ids = ["gragh", "gorgh"];
var newSet = [];
for (var i = 0; i < ids.length; i++) {
newSet[i] = document.getElementById(ids[i]);
}
Loop will finish it's looping much before this onclick executes.So at that time the value of i will be the upper limit of the loop.
A work around of this a closure
var gragh;
var ids = ["gragh", "gorgh"];
for (var i=0; i<ids.length; i++) {
(function(i){ // creating closure
console.log(i)
document.getElementById(ids[i]).onclick = doStuff
})(i) // passing value of i
}
document.getElementById("gragh").innerHTML = "ids[0]: " + ids[0] + ", ids[1]: " + ids[1]
function doStuff() {
document.getElementById("gorgh").innerHTML = "ids[0]: " + ids[0] + ",ids[1]: " + ids[1] + ", var gragh: " + gragh;
}
gragh is undefined since you haveonly declared it but never initialized it
JSFIDDLE

explanation on javascript variables inside a function

I am learning javascript and came across a topic which mentions that the variables declared inside a function is available anywhere inside the function and javascript puts the variable definition at the top like in the below example:
var functionScope=function(){
for (var i=0; i< 10; i++){//code inside this loop}
return i;
}
console.log(functionScope()); //prints 10
The javascript actually turns the above function to the below:
var functionScope=function(){
var i;
for ( i=0; i< 10; i++){//code inside the for loop}
return i;
}
console.log(functionScope()); //prints 10
Since javascript is interpreted language, it executes line by line. How will it know that it should pull the variable to the top of the function after it has tried accessing the variable. When it tries to access the variable it should tell as undefined right?
Also if I go by the theory that the variables will be placed at the top of the function and can be accessed anywhere then the below code should print 10, but why the below code prints undefined?
var functionScope=function(){
console.log('The value os i is '+i);
var i = 20;
}
console.log(functionScope());
Could someone explain where my understanding is wrong?
One more doubt: Typically in Java, if I had to print the value of i outside the for loop, i would get an error, but in javascript does the variable still accessible outside outside the for loop as in case of fist example where the variable is defined inline inside the for loop. Am i missing something here?
var functionScope=function(){
for (var i=0; i< 10; i++);
return i
}
console.log(functionScope()); //prints 10
This is only a bad indentation. Because of the ; at the end of for, it doesn't includes the return i statement into the loop.
There is the well indented one to help you understand what really happens:
var functionScope=function() {
for (var i=0; i< 10; i++)
/* do nothing */;
return i
}
With var, you tell the JS that the variable is not global, and it'll be aviable only inside the function.
With =, you can set the variable's value.
var i; for (i = 0; ... and for (var i = 0; ... is the same.
In the third example, you don't have i inside the function. In this case, the JS'll try to find it as a global variable, outside the function. If you've set window.i = 1, it'll print The value os i is 1, otherwise it'll generate an error, because i is not defined nowhere.
var i = 0;
var fn = function() {
i = 1; //window.i = 1;
};
console.log(i); //prints 1
var i = 0;
var fn = function() {
var i = 1; //fn.i = 1;
}
console.log(i); //prints 0
var fn = function() {
var i = 1; //fn.i = 1;
}
console.log(i); //ReferenceError: i is not defined
var i;
var fn = function() {
var i = 1; //fn.i = 1;
}
console.log(i) //prints undefined
As Sebastien C. has already told you, your example code dosn't do what you want it to do. for (var i=0; i< 10; i++); means for (var i=0; i< 10; i++) {/*do nothing*/}. If you remove the ;, you'll notece your function will return 0, because the return keyword stops the function, and it return the value, no other operations will be executed, your loop will run only once.
Also, you should use ++i.
Yes Javascript is interpreted and whenever is finds a var declared/undeclared, it declares it and then performs the operations or in technical terms it does var hoisting. So now the variable is declared, but is undefined.
So any operation done on it (other than assignment) will result in its value being undefined only. eg;
{
x++ ;
var x = 10 ;
console.log(x);
}
will print 10. So you can think of it as
{
var x = undefined; \\variable hoisted at beginning of block
x++ ;
x = 10 ;
console.log( x ); \\ x = 10
}

Looping through an array and setting variables

What I have so far:
http://codepen.io/anon/pen/umHzl?editors=101
You notice that you can click a box and unclick it. What I would like is that when a certain button is clicked all other buttons are unclicked(turn back to normal color).
My attempt at this:
for (var i =0; i < booths.length; i++){
var obj = booths[i]
obj.e1['fill'] = obj['color'];
obj.e1['checked'] = 'false';
$("#"+obj.name).remove();
}
I know that the color is in the e1/rectangle object of the box, but I do not know how to change/access that variable. It says obj.e1 is undefined. If I do obj['fill'] it still doesn't work.
How would I change the colors from such a loop (or something similar).
It's not e1, it's el (lowercase 'L'). And you'll still want to use the attr() function, e.g.:
for (var i =0; i < booths.length; i++){
var obj = booths[i]
obj.el.attr('fill', obj['color']);
obj.el.attr('checked', 'false');
$("#"+obj.name).remove();
}
Example: http://codepen.io/paulroub/pen/yFwCq

Difference of defining an array inside and outside a for loop

the title is already clear, what's the difference between die create an array inside or outside a for loop.
I will give you an example.
var studentsarray = [];
for(var i = 0; i < 5; i++){
var students = {
id:i,
roll:"9",
age:13
}//end students
studentsarray.push(students);
localStorage.setItem('veritabani', JSON.stringify(studentsarray));
}//end for
var aldim = $.parseJSON(localStorage.getItem('veritabani'));
$.each(aldim, function(i,item){
alert(item.id);
});
if i define inside a for loop, i can't reach all elements, but if i define outside the for loop, it is only the last value of(id) displayed.
Can you explain why?
Thanks in advance.
Few observations:
Javascript only has function level scoping, so defining a variable inside the for loop is equivalent to defining it outside.
However, the variable assignment will happen multiple times if inside the for loop
Please note JSON.stringify is setting studentarray by value multiple times, did you really mean to do this inside the for loop?
I wonder if this is really what you meant to do?
var studentsarray = [];
for(var i = 0; i < 5; i++){
studentsarray.push({id: i, roll:"9", age:13 });
}
localStorage.setItem('veritabani', JSON.stringify(studentsarray));
var aldim = $.parseJSON(localStorage.getItem('veritabani'));
$.each(aldim, function(i,item){
alert(item.id);
});

Javascript 'this' as parameter

I'm trying to copy the value from "FROM" fields into "TO" fields. My first attempt was this:
function updateToField(toField,fromField)
{
toField.value = fromField.value}
}
function verifyFromToFields()
{
var inputs = getElementsByTagName("input");
for (var j = 0; j < inputs.length; j++)
{
if (inputs[j].name.indexOf('FROM') != -1 && if (inputs[j+1].name.indexOf('TO') != -1)
{
var fromField = inputs[j];
var toField = inputs[j+1];
fromField.onchange = function(){updateToField(toField,fromField)};
}
}
The website has several FROM-TO pairs, and this only seem to work for the last pair in the "inputs" array.
Then I tried this:
function updateToField(toField,fromField)
{
toField.value = fromField.value}
}
function verifyFromToFields()
{
var inputs = getElementsByTagName("input");
for (var j = 0; j < inputs.length; j++)
{
if (inputs[j].name.indexOf('FROM') != -1 && if (inputs[j+1].name.indexOf('TO') != -1)
{
var fromField = inputs[j];
var toField = inputs[j+1];
fromField.onchange = function(){updateToField(toField,this)};
}
}
With this, when any FROM field in the page is modified, it's copied to the last TO field on the page. I think this is one of those issues I've read about parameters as value or reference, but I can't figure it out.
Also this is a VERY simplified version of the code, I actually fill the inputs list with a getElementsByClass function and must search through childnodes.
Does anyone have a clue on whats going on?
That closure, I do not think it means what you think it means.
This line here:
fromField.onchange = function(){updateToField(toField,this)};
means "assign to onchange a function that assigns the contents of that fields to whatever toField is at the time of the change!
Since you only have one variable toField all the changeable fields will be assigned to it.
This would work:
var setOnChange = function(fromField, toField) {
fromField.onchange = function(){updateToField(toField,this)};
};
for (var j = 0; j < inputs.length; j++)
{
if (inputs[j].name.indexOf('FROM') != -1 && if (inputs[j+1].name.indexOf('TO') != -1)
{
setOnChange(inputs[j], inputs[j+1]);
}
}
EDIT: Isaac might have a better explanation of the problem (although I don't really like his solution).
That's is because of how closures work. When you assign the onchange function you create (for every loop) a new anonymous function that calls updateToField, but the value of the toField and this parameters (or any other you are passing) are not bind to the "current" loop, instead that parameters are bind to the last value of the loop (that's why is only working with your last "TO").
Instead of assigning a new function to onchange property try calling the Function.bind if you're running in an environment that has that or write one if you don't have it.
Here are the documentation for bind
So you can go like this:
fromField.onchange = updateToField.bind(this, fromField, toField);
Or you can use the other approach that Malvolio wrote.
The problem actually has to do with scope. What's happening is that your functions (the ones you assigned to onchange) are capturing the variables toField and fromField and their values keep changing. I know it looks like you've declared them anew each time through the loop, but that's not how JS works; consecutive trips through a loop share a scope, so fromField is the same variable each time through and you're merely assigning it a new value in each iteration. So at the end, all of your functions refer to the same fromField variable. And that fromField variable, naturally, contains the last value you assigned it.
So when you eventually call all of those functions, they all do the same thing, because all of their fromFields (and, by the same logic, toFields) are the same variable. So that explains why only the last inputs worked; they're what fromField and toField contained when you ran the functions.
You can fix this by introducing an intermediate function, since functions do create new scopes. That way each time through the loop, you get brand new variables.
function updateToField(toField,fromField)
{
toField.value = fromField.value;
}
function verifyFromToFields()
{
var inputs = getElementsByTagName("input");
for (var j = 0; j < inputs.length; j++)
{
function(){
if (inputs[j].name.indexOf('FROM') != -1 && if (inputs[j+1].name.indexOf('TO') != -1)
{
var fromField = inputs[j];
var toField = inputs[j+1];
fromField.onchange = function(){updateToField(toField,fromField)};
}
}();
}

Categories

Resources