2

I've been trying to understand why whenever value of the array I click, it always add the class "foo".

Example: I clicked on London (cities[1], right?) and it added the class foo.

var cities = [
document.getElementById('Paris'),
document.getElementById('London'),
document.getElementById('Berlin')
];


for (var i = 0; i < cities.length; i++) {
  cities[i].onclick = test;

  function test(){
    if(cities[i] === cities[0]) {
      el.classList.add("foo");
    }
  }
}
3
  • What is the value of el? Commented Apr 29, 2014 at 18:59
  • the value el is document.getElementById('el'); Commented Apr 29, 2014 at 19:01
  • As far as I can see it should never add the class foo instead of always since cities[3] === cities[0] will never be true. Also you should not be using a function declaration within blocks and be cautious with how function expressions handles closures within loops. Commented Apr 29, 2014 at 19:14

4 Answers 4

2

EDIT: my original answer was incorrect, this updated one is right. addEventListener returns nothing. Instead, you should use some kind of wrapper to add and remove your listeners, again so that you don't waste resources on listeners that you aren't using:

function on (element, eventName, callback) {
    element.addEventListener(eventName, callback);
    return function unregister () {
        element.removeEventListener(callback);
    }
}

function test (event) {
    if (event.currentTarget===cities[0]) {
        event.target.classList.add('foo');
    }
}

var listenerRemovers = cities.map(function (city) {
    return on(city, 'click', test);
});

Now you can remove any of these listeners by calling the corresponding function in your listenerRemovers array:

listenerRemovers.forEach(function (unRegisterFunc) { unRegisterFunc(); });

ORIGINAL WRONG ANSWER:

For what it's worth, you're probably better off using .map in a case like this, since best practice is to keep a reference to the event listeners so you can cancel them if needed.

function test (event) {
    if (event.currentTarget===cities[0]) {
        event.target.classList.add('foo');
    }
}

var listenerHandlers = cities.map(function (city) {
  return city.addEventListener('click', test);
});
Sign up to request clarification or add additional context in comments.

1 Comment

Tried that way but then if I add an else if condition, it dont work :(
1

This is happening because you are setting the event functions inside a loop. Each function is using the same value of i.

Try to use this instead of trying to cities[i] inside the function.

function test(){
    if(this === cities[0]) {
      el.classList.add("foo");
    }
}

Comments

0

The easiest approach to achieve this functionality is to use jQuery, here is the idea:

  1. In html tags, give those cities a common class, e.g. class="city"
  2. $('.city').click(function(){$('.city').addClass('foo')});

jQuery saves you more time and coding efforts.

Comments

-1

The problem is you are trying to assign a function to a DOM attribute. You are not registering a listener but modifying the DOM. If you wish to do it this way, you must assign the onclick as cities[i].onclick = 'test()'

Also, you should move the function test outside of the for loop to look like the following. The problem is the function test is being declared many times, each with a different 'i' value.

for (var i = 0; i < cities.length; i++) {
  cities[i].onclick = 'test(this)';
}

function test(el){
 if(cities[i] === cities[0]) {
    el.classList.add("foo");
 }
}

1 Comment

You can set the .onclick attribute to a function. developer.mozilla.org/en-US/docs/Web/API/…

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.