0

Having to do some UI work this week... not something I am used to! Basically I have an array of objects (in this case simplified as integers) and need to loop through them to find a matching element on the page and attach an event handler with the data from that part of the array. I think this is a scoping issue - then I played around assigning _local = this; it always seemed that the last item in the loop was attached to the handler (i.e. it would always say the last value has been clicked for all the buttons)

$(document).ready(function () {

var filters = [0,1,2,3];

// Works as expected 
// When button 0 is clicked log message is "btn0 clicked!"
// When button 1 is clicked log message is "btn1 clicked!"
// Etc

$('#btn0').on('click', function () {
    console.log("bt0 clicked");         
} );

$('#btn1').on('click', function () {
    console.log("bt1 clicked");         
} )

$('#btn2').on('click', function () {
    console.log("bt2 clicked");         
} )

$('#btn3').on('click', function () {
    console.log("bt3 clicked");         
} )



// Undefined all over the place
for (var i = 0; i < filters.length; i++) {
    $('#btn' + filters[i].toString()).on('click', function () {
        console.log("bt" + filters[i].toString() + " clicked");         
    } );
}
});
Legionar
  • 7,472
  • 2
  • 41
  • 70
djangoat
  • 475
  • 4
  • 12
  • What is your question? – Dmytro Shevchenko Dec 15 '15 at 10:08
  • I'm quite sure you don't need any loop to bind event. Just looks like you want to use `this` inside click handler. Better would be to use common class for all your buttons but here, you could still use: `$('[id^=btn]').on('click', function () { console.log(this.id + " clicked"); } );` – A. Wolff Dec 15 '15 at 10:13
  • you can't use filters[i] inside because filters[i] end the loop and when you click the button, the filters[i] has the same value for all the buttons. You need to use $(this) and get the "id" for example with $(this).attr('id') – ZiTAL Dec 15 '15 at 10:17

3 Answers3

1

You are right. You have to read how closures works in javascript. I would suggest a simple solution comes with ES6 using let. Also you don't need to use .toString. When you using unary plus operator number converts to string behind the scenes("btn" + 1// btn1).

  'use strict';
  var filters = [0, 1, 2, 3];
  for (let i = 0; i < filters.length; i++) {
    $('#btn' + filters[i]).on('click', function() {
      console.log("bt" + filters[i] + " clicked");
    });
  }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id='btn0'>bt0</button>
<button id='btn1'>bt1</button>
<button id='btn2'>bt2</button>
<button id='btn3'>bt3</button>

Pre ES6 you can create an IIFE:

var filters = [0, 1, 2, 3];
for (var i = 0; i < filters.length; i++) {
  (function(i) {
    $('#btn' + filters[i]).on('click', function() {
      console.log("bt" + filters[i] + " clicked");
    });
  })(i);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id='btn0'>bt0</button>
<button id='btn1'>bt1</button>
<button id='btn2'>bt2</button>
<button id='btn3'>bt3</button>
Alex Char
  • 32,879
  • 9
  • 49
  • 70
0

As an alternative to @Alex answer.

You can persist custom using the data() with individual elements which can later be fetched in the vent handler.

'use strict';
var filters = [0, 1, 2, 3];

for (var i = 0; i < filters.length; i++) {
  //Set data and class
  $('#btn' + filters[i]).data('item', filters[i]).addClass('btn');
}

//Bind Click handler
$('.btn').on('click', function() {
  snippet.log("bt" + $(this).data('item') + " clicked");
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!-- Provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
<button id='btn0'>bt0</button>
<button id='btn1'>bt1</button>
<button id='btn2'>bt2</button>
<button id='btn3'>bt3</button>
Satpal
  • 132,252
  • 13
  • 159
  • 168
0

https://developer.mozilla.org/es/docs/Web/JavaScript/Closures

$(document).ready(function () {

  var filters = [0,1,2,3];

  for (var i = 0; i < filters.length; i++) {
      createOnclick(filters[i]);
  }
  
  function createOnclick(elm) {
    return function() {
        $('#btn' + elm.toString()).on('click', function () {
          console.log("bt" + elm.toString() + " clicked");         
        });
    
    }();
  }
  
  
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="button" id="btn0" value="0" />
<input type="button" id="btn1" value="1" />
<input type="button" id="btn2" value="2" />
<input type="button" id="btn3" value="3" />
jolmos
  • 1,565
  • 13
  • 25