0

I have different divs, each of them having a button calling the same function.

<div id="div1">. . .</div>
<button id="btn1" > INIT </button>
<div  id="div2">. . .</div>
<button id="btn2"> INIT </button>

Tricky part is that each button should only execute the function to his own div (#btn1 to #div1, #btn2 to #div2, etc) and not to the others. The code I wrote executes the same function to every div without me clicking a button. I guess the answer is easy but I can't figure what I'm doing wrong.

var b = 1;

for (var b = 1; b < 5 ; b++) {
    document.getElementById("btn" + b).onclick = 
    document.getElementById("div" + b).innerHTML = "HELLO WORLD";
}

fiddle : https://jsfiddle.net/balleronde/tr8wntmL/

How can I execute the function to #div1 only when clicking #btn1, and not to all at once ? thanks

Nora
  • 185
  • 1
  • 1
  • 9

3 Answers3

2

Here's your code:

document.getElementById("btn" + b).onclick = 
document.getElementById("div" + b).innerHTML = "HELLO WORLD"

As far as JavaScript knows, that looks like this to it:

x = y = "HELLO WORLD"

Here's what happens when you do that:

x =
y = "hello world"

console.log("x is:", x)
console.log("y is:", y)

So your code ends up setting the innerHTML property of #div(b) and the onclick property of #btn(b)! Whoops, that's not what you want.

What you really want is for #div(b)'s innerHTML to be set when you click on #btn(b).

To do that, you need to assign a function, or a callback, to #btn(b)'s onclick property:

function setTheText(b) {
  document.getElementById('div' + b).innerHTML = 'HELLO WORLD'
}

for (var b = 1; b < 5; b++) {
  document.getElementById('btn' + b).onclick = function() {
    setTheText(b)
  }
}
<div id='div1'>Div 1</div>
<div id='div2'>Div 2</div>
<div id='div3'>Div 3</div>
<div id='div4'>Div 4</div>
<button id='btn1'>Button 1</button>
<button id='btn2'>Button 2</button>
<button id='btn3'>Button 3</button>
<button id='btn4'>Button 4</button>

Hmm.. we get an error when we click on the buttons: "Uncaught TypeError: Cannot set property innerHTML of null". Whoops!

Let's investigate. Here's what happens when we replace the setTheText function's code with something that logs b:

function setTheText(b) {
  console.log(b)
}

for (var b = 1; b < 5; b++) {
  document.getElementById('btn' + b).onclick = function() {
    setTheText(b)
  }
}
<button id='btn1'>Button 1</button>
<button id='btn2'>Button 2</button>
<button id='btn3'>Button 3</button>
<button id='btn4'>Button 4</button>

It always outputs 5! That doesn't make much sense.

Basically the issue is b gets changed to 5 by the for loop before JavaScript calls your onclick function.

So what we really need to do is use a function to create the callback function, so that the callback function can remember what index (b) it was created with:

function makeSetTheText(b) {
  function setTheText() {
    document.getElementById('div' + b).innerHTML = 'HELLO WORLD'
  }
  return setTheText
}

for (var b = 1; b < 5; b++) {
  document.getElementById('btn' + b).onclick = makeSetTheText(b)
}
<div id='div1'>Div 1</div>
<div id='div2'>Div 2</div>
<div id='div3'>Div 3</div>
<div id='div4'>Div 4</div>
<button id='btn1'>Button 1</button>
<button id='btn2'>Button 2</button>
<button id='btn3'>Button 3</button>
<button id='btn4'>Button 4</button>

It that's still a bit confusing, don't worry, there's a bunch of really good explanations on how closures and functions that generate functions on StackOverflow here.

If you've made it this far, hopefully:

  1. You'll have learned a bit about debugging.
  2. You'll have figured out a solution to your problem.
  3. You'll have learned a couple fancy JavaScript tricks.
  4. You'll have some sympathy for the amount of lag I had to deal with while typing this answer, and all the research and testing I did.

Hope this helps!

Community
  • 1
  • 1
Nebula
  • 6,614
  • 4
  • 20
  • 40
1

I assume you want something like this:

Create function-handler with parameter

function myAwesomeHandler(param){
   document.getElementById("div" + param).innerHTML = "HELLO WORLD";
};

Bind this handler, so you handler will be called with counter value as parameter. Check documentation about bind

for (var b = 1; b < 5 ; b++) {
    document.getElementById("btn" + b).onclick = myAwesomeHandler.bind(this, b);
}

Although there are more elegant ways with different html, I assumed you have predefined html structure and just demonstrated "only js" method.

Daydreaming Duck
  • 2,110
  • 3
  • 18
  • 25
1

try this,

<div id="div1">. . .</div>
<button id="btn1" onclick="changeText('div1')"> INIT </button>
<div  id="div2">. . .</div>
<button id="btn2" onclick="changeText('div2')"> INIT </button>

and create a function,

function changeText(divId){
        document.getElementById(divId).innerHTML = 'some text';
    }
Asarudeen
  • 85
  • 2