mehowow
mehowow

Reputation: 11

Javascript. Onclick returns always the same object

I have got 16 divs of the same class name in html document in the following fashion

    <div class="game-selection-tab">
      <h2>30m + 15s</h2>
    </div>
    <div class="game-selection-tab">
      <h2>60m + 0s</h2>
    </div>
    <div class="game-selection-tab">
      <h2>Custom</h2>
    </div>

I want to create onclick method that returns h2 text content from particular div. I tried to solve this by using following javascript code.

var selectionTabs = document.getElementsByClassName("game-selection-tab");

for(var i = 0; i < selectionTabs.length; i++){

  var tab = selectionTabs[i];
  var content = tab.getElementsByTagName("h2");
  tab.onclick = function(){
   console.log(content[0].textContent);
  }
}

The problem is: no matter which div i click, program always returns h2 text content from the last div(in this example "custom").

Upvotes: 0

Views: 410

Answers (6)

luenib
luenib

Reputation: 370

Assuming that you don't wanna change your HTML to include an "onclick" event on each h2, this code might help you:

document.addEventListener('click', function(e) {
  e = e || window.event;
  var target = e.target || e.srcElement,
    text = target.textContent || text.innerText;
        console.log(text);
}, false);

EDIT If you want to be more specific and get the content from only your h2's, you could use this:

h2s = document.getElementsByTagName('h2');
for (var i = 0; i < h2s.length; i++) {
  h2s[i].addEventListener('click', redirect, false);
}

function redirect(e) {
  var target = e.target || e.srcElement;
  var text = target.textContent || text.innerText;
  console.log(text);
}

Upvotes: 0

Sachin Singh
Sachin Singh

Reputation: 948

Try this simple solution:

var els = document.getElementsByClassName('game-selection-tab');
var index = 0;

function getText() {
  alert(this.innerText || this.textContent);
}

for (; index < els.length; index++) {
  els[index].onclick = getText;
}
<div class="game-selection-tab">
  <h2>30m + 15s</h2>
</div>
<div class="game-selection-tab">
  <h2>60m + 0s</h2>
</div>
<div class="game-selection-tab">
  <h2>Custom</h2>
</div>

Upvotes: 0

Jolaade Adewale
Jolaade Adewale

Reputation: 336

Can you try the solution below.

  var selectionTabs = document.getElementsByClassName("game-selection-tab");

     Object.keys(selectionTabs).forEach((data, index) => {
       var context = selectionTabs[data].getElementsByTagName("h2")[0].textContent;
       selectionTabs[data].onclick = function () {
         console.log(context)
       }
     })

Upvotes: 0

connexo
connexo

Reputation: 56744

Replace

var i = 0

by

let i = 0

and you're done.

A detailed explanation is here.

I'll quote my answer for your understanding below.

Cause of the problem: lack of understanding scope

Check this example to understand the problem:

var creates function scope

var funcs = []

for (var i = 0; i < 10; i++) {
  funcs.push(function() {
    console.log(i)
  })
}

funcs.forEach(function(func) {
  func()
})

While you might expect this forEach loop to result in number 0 to 9 being printed, instead you get ten times 10. The cause of this is the variable i being declared using var keyword, which creates a function scope that leads to each function in funcs holding a reference to the same i variable. At the time the forEach loop is executed, the previous for-loop has ended and i holds 10 (9++ from the last iteration).

Compare how ES6's let, which creates block scope instead of function scope, behaves in this regard:

let (ES6 or officially ES2015) creates block scope:

var funcs = []

for (let i = 0; i < 10; i++) {
  funcs.push(function() {
    console.log(i)
  })
}

funcs.forEach(function(func) {
  func()
})

Because let creates block scope, each iteration of the for loop has its "own" variable i.

ES5 solution using an IIFE wrapper

If you need an ES5 solution, an IIFE (immediately invoked function expression) wrapper would be the way to go:

var funcs = []

for (var i = 0; i < 10; i++) {
  funcs.push((function(value) {
    return function() {
      console.log(value)
    }
  }(i)))
}

funcs.forEach(function(func) {
  func()
})

Here, i is passed as a parameter to each function which stores its own copy value.

The same is true for for..in loops:

var funcs = [],
  obj = {
    first: "first",
    last: "last",
    always: "always"
  }
  
for (var key in obj) {
  funcs.push(function() {
    console.log(key)
  })
}

funcs.forEach(function(func) { // outputs: "always", "always", "always"
  func()
})

Again, all functions in funcs hold the reference to the same key because var key creates a function scope that lives outside of the for..in loop. And again, let produces the result you'd probably rather expect:

var funcs = [],
  obj = {
    first: "first",
    last: "last",
    always: "always"
  }
  
for (let key in obj) {
  funcs.push(function() {
    console.log(key)
  })
}

funcs.forEach(function(func) {
  func()
})

Also compare the excellent (!) book

Nicholas C. Zakas: "Understanding ES6", no starch press, p. 8-9.

from which the examples were taken.

Upvotes: 2

ztadic91
ztadic91

Reputation: 2804

It's showing always the same value because you are setting content outside of the onclick function. After the for loop, content points to the last h2.

Move the content definition inside the onclick function.

  tab.onclick = function(){
    var content = this.getElementsByTagName("h2");
    console.log(content[0].textContent);
  }

Working fiddle

Upvotes: 1

Akshay Khandelwal
Akshay Khandelwal

Reputation: 1570

Try this

var selectionTabs = document.getElementsByClassName("game-selection-tab");

for(var i = 0; i < selectionTabs.length; i++){
  (function (index) {
      var tab = selectionTabs[index];
      var content = tab.getElementsByTagName("h2");
      tab.onclick = function(){
       console.log(content[0].textContent);
      }
  })(i);
}

The thing is by the time your event attaches to the actual DOM element the for loop execution is complete and the value of i is the max value that it can reach. Hence, isolating the same in a function like this works. The function stores the value of i or in this case index as the original value that you expect.

Upvotes: 2

Related Questions