0

I have multiple buttons with the ids "especifica-i-pj", where i goes from 1 to 4 and j goes from 1 to 3. The idea is to assign an onclick event to each one of them, without having to declare 12 functions. I thought this would solve my problem:

for ( i = 1; i <= 4; i++ ) {
    for ( j = 1; j <= 3; j++ ) {
        dom_id = "especifica-"+i.toString()+"-p"+j.toString();
        document.getElementById(dom_id).onclick = function() {
            console.log("you clicked ",dom_id)
            ponder[i-1] = 0.1 + 0.05*(j-1);
            console.log("ponder:",i,ponder[i-1])
            $("#"+dom_id).addClass("active");
            for ( k = 1; k <= 3 && k != j; k++ )
                $("#especifica-"+i.toString()+"-p"+k.toString()).removeClass("active");
        }
    }
}

However each time I click any of the buttons, my console outputs that I clicked especifica-4-p3, the last one, and outputs ponder: 5 0.25, when i==5 should not be possible, as the outer for gets to i==4.

What can I do to make this work? I have been searching online but I have not found anything about this, or even declaring functions inside for loops. Thanks.

mianfg
  • 15
  • 8
  • No, i gets to 5, and then exits the for loop. It is still 5 at the time of the click event. You likely want to store the current value of i into a different non-global variable so that you can reference it inside your function. – Robert McKee Apr 16 '20 at 11:23
  • What does your `HTML` look like? I'd be helpful to see that as well – goto Apr 16 '20 at 11:24

3 Answers3

2

The easy way would be to do one of the following:

  1. Store the values of i and j on the DOM element.
  2. Parse the clicked id to get the values of i and j.
  3. Assign the values of i and j to non-global variables so that you can retrieve them later.

Method 1:

for(i=1; i<=4; i++) {
  for(j=1; j<=3; j++) {
    dom_id = "especifica-"+i.toString()+"-p"+j.toString();
    element = document.getElementById(dom_id);
    element.i = i;
    element.j = j;
    element.onclick = function() {
      console.log("you clicked ",dom_id)
      ponder[this.i-1] = 0.1 + 0.05*(this.j-1);
      console.log("ponder:",this.i,ponder[this.i-1])
      $("#"+dom_id).addClass("active");
      for(k=1; k<=3 && k!=j; k++)
        $("#especifica-"+this.i.toString()+"-p"+k.toString()).removeClass("active");
    }
  }
}

Method 2:

for(i=1; i<=4; i++) {
  for(j=1; j<=3; j++) {
    dom_id = "especifica-"+i.toString()+"-p"+j.toString();
    document.getElementById(dom_id).onclick = function() {
      i = dom_id.substring(11,12);
      j = dom_id.substring(14,15);
      console.log("you clicked ",dom_id)
      ponder[i-1] = 0.1 + 0.05*(j-1);
      console.log("ponder:",i,ponder[i-1])
      $("#"+dom_id).addClass("active");
      for(k=1; k<=3 && k!=j; k++)
        $("#especifica-"+i.toString()+"-p"+k.toString()).removeClass("active");
    }
  }
}

Method 3:

for(i=1; i<=4; i++) {
  let i1 = i;
  for(j=1; j<=3; j++) {
    let j1 = j;
    dom_id = "especifica-"+i.toString()+"-p"+j.toString();
    document.getElementById(dom_id).onclick = function() {
      console.log("you clicked ",dom_id)
      ponder[i1-1] = 0.1 + 0.05*(j1-1);
      console.log("ponder:",i,ponder[i1-1])
      $("#"+dom_id).addClass("active");
      for(k=1; k<=3 && k!=j; k++)
        $("#especifica-"+i1.toString()+"-p"+k.toString()).removeClass("active");
    }
  }
}
Robert McKee
  • 21,305
  • 1
  • 43
  • 57
  • How do I do 1)? – mianfg Apr 16 '20 at 11:32
  • Could you show me howto? I've been searching and found nothing. Thanks in advance! – mianfg Apr 16 '20 at 11:37
  • You can also make a local function and call it, which would be yet another way. I suspect that you could also use the position of the elements to determine i and j and use a single event handler, but we'd need to see the html to do so. – Robert McKee Apr 16 '20 at 11:41
1

The best way is to capture on click for the first level parent which is the parent for all the buttons,

Only for that you create on click,

inside the event function detect event.target which will tell from which button the click is happening

function buttonClick(event) {
    alert(event.target.getAttribute("id"));
}
<div id="parent" onclick="buttonClick(event)">
   <button id="especifica-1-p1">b11</button>
   <button id="especifica-1-p2">b12</button>
   <button id="especifica-1-p3">b13</button>
   <button id="especifica-2-p1">b21</button>
   <button id="especifica-2-p2">b22</button>
   <button id="especifica-2-p3">b23</button>
</div>

If you want to maintain the data 1,1 1,2 ... etc , you can do like this

In each button you can introduce dummy data attributes with your own name assumption

Example

<button id="especifica-1-p1" data-row="1" data-column="1">b11</button>
<button id="especifica-1-p2" data-row="1" data-column="2">b12</button>

Then you can use JavaScript

event.target.getAttribute("data-row");
event.target.getAttribute("data-column");
Dickens A S
  • 3,824
  • 2
  • 22
  • 45
1

Here's a fix by using data-* attributes:

for (var i = 1; i <= 2; i++) {
  for (var j = 1; j <= 3; j++) {
    var buttonId = "especifica-" + i + "-p" + j;
    var button = document.getElementById(buttonId);
    
    button.setAttribute("data-i", i);
    button.setAttribute("data-j", j);

    button.addEventListener("click", function(event) {
      console.log("i", event.target.getAttribute("data-i"));
      console.log("j", event.target.getAttribute("data-j"));
      console.log("button with id of", event.target.id, "was clicked!");
    });
  }
}
<div><button id="especifica-1-p1" type="button">especifica-1-p1</button></div>
<div><button id="especifica-1-p2" type="button">especifica-1-p2</button></div>
<div><button id="especifica-1-p3" type="button">especifica-1-p3</button></div>
<div><button id="especifica-2-p1" type="button">especifica-2-p1</button></div>
<div><button id="especifica-2-p2" type="button">especifica-2-p2</button></div>
<div><button id="especifica-2-p3" type="button">especifica-2-p3</button></div>

Learn more about data-* attributes here:


Alternatively, you could take advantage of the concept of closures, like in the following example:

for (var i = 1; i <= 2; i++) {
  for (var j = 1; j <= 3; j++) {
    var buttonId = "especifica-" + i + "-p" + j;
    var button = document.getElementById(buttonId);
    
    var createEventListener = function (iValue, jValue) {
      return function (event) {
        console.log("i:", iValue, "j:", jValue);
        console.log("button with id of", event.target.id, "was clicked!");
      }
    };
    button.addEventListener("click", createEventListener(i, j));
  }
}
<div><button id="especifica-1-p1" type="button">especifica-1-p1</button></div>
<div><button id="especifica-1-p2" type="button">especifica-1-p2</button></div>
<div><button id="especifica-1-p3" type="button">especifica-1-p3</button></div>
<div><button id="especifica-2-p1" type="button">especifica-2-p1</button></div>
<div><button id="especifica-2-p2" type="button">especifica-2-p2</button></div>
<div><button id="especifica-2-p3" type="button">especifica-2-p3</button></div>

Read more about closures here:

Also, use the element.addEventListener method instead of element.onclick. Have a look at here why it's better to use addEventListener('click', ...) instead of onclick:

goto
  • 4,336
  • 15
  • 20
  • how can I use the i, j values inside the function without parsing the string? – mianfg Apr 16 '20 at 11:40
  • @mianfg If you're talking about having access to `i` and `j` inside of the event handler, then they're both in the scope of that handler function, so you can just use them however you'd like. See my edit. – goto Apr 16 '20 at 11:43
  • @goto1 Change your `var i` to `let i` and `var j` to `let j` to fix it so you can reference the current value of i and j inside the function. – Robert McKee Apr 16 '20 at 11:50