0

I have this code for example:

<html>
<input class="basketQuantity" type=number>Basket item 1</input>
<input class="basketQuantity" type=number>Basket item 2</input>
</html>

<script>
quantityInputs = document.querySelectorAll(".basketQuantity");
for(var i in quantityInputs){
    quantityInputs[i].addEventListener('change', function(){
        console.log(quantityInputs[i]);
    });
}
</script>

When i run it i get the error message:

TypeError: quantityInputs[i].addEventListener is not a function

Could anyone tell me why this is happening?

Adam
  • 1,489
  • 2
  • 10
  • 11
  • There are a couple of properties in the [NodeList](https://developer.mozilla.org/en-US/docs/Web/API/NodeList), which are counted when using `for..in`. Just use a `for` loop to iterate the list, not `for..in`. Notice, that your HTML is invalid, [``](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Input) must not have a closing tag. – Teemu Apr 09 '15 at 18:44
  • Your for loop is not correct. In your case 'i' is a String. So what you are doing is: quantityInputs["0"] and quantityInputs["1"]. Instead of integers quantityInputs[0] and quantityInputs[1]. – Hoyen Apr 09 '15 at 19:00
  • After you've got the loop working, [here](http://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example) is a solution to your second problem. – Teemu Apr 09 '15 at 19:14

3 Answers3

1
quantityInputs = document.querySelectorAll(".basketQuantity");

First of all quantityInputs is the NodeList object. So

for(var i in quantityInputs){
    console.log(i);
}

will return all enumerable properties - from quantityInputs object and it prototype chain, not only its own (just quantityInputs enumerable properties). for .. in will return also length field and item enumerable properties from prototype chain and this properties are not DOM nodes and so don't have addEventListener method.

You must use Object.keys:

var nodeArray = [].slice.call(document.querySelectorAll(".basketQuantity"));

Object.keys(nodeArray).forEach(function (node) {
    // node.addEventListener
});

OR

or for .. in with hasOwnProperty check:

quantityInputs = document.querySelectorAll(".basketQuantity");
for(var i in quantityInputs){
    if (quantityInputs.hasOwnProperty(i)) {
        // quantityInputs[i].addEventListener
    }
}

OR

In the future (ES6) you can use in this case for .. of loop:

var quantityInputs = document.querySelectorAll("basketQuantity");

for (var node of quantityInputs) {
    console.log(node.addEventListener);
}

Note(thanks to @Teemu):

Also you have a error in your handler with i:

quantityInputs[i].addEventListener('change', function(){
    console.log(quantityInputs[i]);// return value of quantityInputs last i
});

so better use this for addEventListener target:

quantityInputs[i].addEventListener('change', function(){
    console.log(this);
});
Alex
  • 11,115
  • 12
  • 51
  • 64
0

You are iterating on a nodeList:

 quantityInputs = document.querySelectorAll(".basketQuantity");
    [].forEach.call(quantityInputs, function (e) {
        e.addEventListener('click', function () {
            console.log("hello")
        }, false)
    })
Ofer Haber
  • 618
  • 4
  • 10
0

The JavaScript for/in statement loops through the properties of an object, so your var i sometimes receives properties of the array quantityInputs, like length.

Just change your loop to a convencional, like for (i = 0; i < quantityInputs.length; i++) {... and it should works:

quantityInputs = document.querySelectorAll(".basketQuantity");
for (i = 0; i < quantityInputs.length; i++) {
    quantityInputs[i].addEventListener('change', function(){
        console.log('foo');
    });
}

Take a look here for more information: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in

Victor
  • 5,043
  • 3
  • 41
  • 55
  • 1
    This is just an example, but if you need `quantityInputs[i]` then `this` is equivalent in this context, so you can use `console.log(this)` – Victor Apr 09 '15 at 19:17