Почему событие срабатывает только для последнего элемента? [дубликат]

70
12 июня 2021, 14:30
На этот вопрос уже даны ответы здесь:
Почему асинхронная функция внутри цикла выполняет последнюю итерацию много раз? (2 ответа)
Закрыт 1 год назад.

Есть массив объявлений, к каждому из которых генерируется метка на карте и карточка к ней. При клике на метку, должна отобразиться соответствующая ей карточка. Следующий код срабатывает как надо:

 adsList.forEach(item => {
                var pin = renderPin(item);
                var ad = window.card.renderAd(item);
                pin.addEventListener('click', function (evtClick) {                  
                    window.pin.openedPin.classList.add("map__pin--active");
                    ad.classList.remove("hidden");
                })
            });

Но чтобы ограничить число меток на карте, я хочу воспользоваться обычным циклом for и получается следующее:

 for(var i = 0; i < PIN_NUMBER; i++){
                var pin = renderPin(adsList[i]);
                var ad = window.card.renderAd(adsList[i]);
                pin.addEventListener('click', function (evtClick) {                  
                    window.pin.openedPin.classList.add("map__pin--active");
                    ad.classList.remove("hidden");
                })
            });

В этом случае при клике на любую метку отображается та карточка, которая была последней в цикле. Может быть, кто-нибудь может объяснить с чем связано такое поведение?

Answer 1

Здесь не важно, какой код, важно поведение var в цикле, поэтому примеры будут с другим кодом. Допустим, навешиваем событие на кнопки, а при клике - они должны что-то делать с числом i:

var bubu = document.querySelectorAll('.bubu'); 
 
for( var i = 0; i < bubu.length; i++ ){ 
  bubu[i].addEventListener('click', function(){ 
    console.log( i ); 
  }); 
} 
console.log( i );
<button class="bubu">0000</button> 
<button class="bubu">1111</button> 
<button class="bubu">2222</button> 
<button class="bubu">3333</button> 
<button class="bubu">4444</button>

Хотелось, чтобы каждая кнопка выводила свой номер - а выводит 5. Это потому что цикл практически моментально сработав - вешает обработчики на все кнопки и заканчивает свою работу. А уже потом, в момент клика, i даже вне цикла, для всех равен 5.

Если не хочется использовать let, можно "поймать" значение i через запущенную на месте функцию:

var bubu = document.querySelectorAll('.bubu'); 
 
for( var i = 0; i < bubu.length; i++ ){ 
  (function(i){ 
    //Здесь уже i - не тот же i из цикла, а уже внутренняя переменная для функции 
    //и она равна... 
    bubu[i].addEventListener('click', function(){ 
      console.log( i ); 
    }); 
  })(i);//этому i, который получен прямо из цикла, в момент его выполнения. 
} 
console.log( i );
<button class="bubu">0000</button> 
<button class="bubu">1111</button> 
<button class="bubu">2222</button> 
<button class="bubu">3333</button> 
<button class="bubu">4444</button>

А let на каждом круге цикла - заново создается и "виден" только на этом круге цикла. Вне цикла - вообще будет undefined:

var bubu = document.querySelectorAll('.bubu'); 
 
for( let i = 0; i < bubu.length; i++ ){ 
  bubu[i].addEventListener('click', function(){ 
    console.log( i ); 
  }); 
} 
console.log( i ); // ошибка, i is not defined
<button class="bubu">0000</button> 
<button class="bubu">1111</button> 
<button class="bubu">2222</button> 
<button class="bubu">3333</button> 
<button class="bubu">4444</button>

Answer 2

// es6 
 
  const bubu_parent = document.querySelector('.bubu_parent'); 
 
  const buttons = bubu_parent.querySelectorAll('button'); 
 
  bubu_parent.addEventListener('click', (e)=> { 
 
    const result = Array.from(buttons).findIndex(el => el === e.target); 
 
       console.log(result); 
   }); 
 
  
 <div class="bubu_parent"> 
    <button class="bubu">0000</button> 
    <button class="bubu">1111</button> 
    <button class="bubu">2222</button> 
    <button class="bubu">3333</button> 
    <button class="bubu">4444</button> 
</div> 
 
 
  

READ ALSO
Как сохранить место прокрутки страницы после перезагрузки?

Как сохранить место прокрутки страницы после перезагрузки?

Я пользуюсь VS Code и там же запускаю localhost для авто перезагрузки страницы (после сохранения файла)Но проблема в том, что после этой перезагрузки...

78
Как вызвать методы компонента VueJs

Как вызвать методы компонента VueJs

Создаю компонент диалога

102
Алгоритм поиска подстроки в массиве строк (Javascript)

Алгоритм поиска подстроки в массиве строк (Javascript)

Необходимо реализовать алгоритм проверяющий наличие подстроки в массиве строк и возвращающая номера элементов массива, в которых встретилась...

82