Javascript: функция onclick в цикле for не разрешает переменную

Я пишу подпрограмму на Javascript, которая перебирает все аудиоэлементы на HTML-странице. Все элементы отмечены идентификатором в виде «track_[nr]», и каждый из них имеет настраиваемую кнопку, которая переключает воспроизведение/паузу, отмеченную идентификатором «control_[nr]».

Теперь я хочу автоматизировать событие onclick для каждого элемента управления, перебирая все элементы и назначая функцию onclick.

Я придумал следующий код, но цикл for ведет себя не так, как я ожидал.

«document.writeln(j)» всегда печатает 5 (в настоящее время у меня 5 аудиоэлементов), независимо от того, какой элемент управления я нажимаю. Я ожидаю, что он напишет «0», когда вы нажмете «control_0», «1» при нажатии на «control_1» и т. д.

Любая помощь высоко ценится!

<script type='text/javascript'>
    var audio = new Array();
    var ctrl = new Array();
    var i = 0;

    do {
        audio[i] = document.getElementById('track_'+i), ctrl[i] = document.getElementById('control_'+i);
        i++;
    } while(audio[i-1]);
    tracks=i-1;

    for (var j = 0; j < tracks; j++) {
        ctrl[j].onclick = function () {
            document.writeln(j);
        }
    }
</script>

С Уважением


person vitaminx    schedule 23.03.2015    source источник
comment
ммм, если это не просто опечатка, то закрывающие скобки вашего цикла for отсутствуют.   -  person KBN    schedule 23.03.2015
comment
что пропало во время копирования и вставки, я исправил это.   -  person vitaminx    schedule 23.03.2015


Ответы (4)


Во-первых, у вас есть «control_», а не «ctrl_» в JS! Однако ваша основная проблема заключается в том, что функция onclick является закрытием, что означает, что она не использует значение j в своем определении, она использует фактическую переменную j, которая изменяется циклом for. Вам нужно создать функцию, которая принимает j в качестве параметра и возвращает обработчик. Это работает, потому что значение j передается в функцию.

var audio = new Array();
    var ctrl = new Array();
    var i = 0;

    do {
      audio[i] = document.getElementById('track_' + i), ctrl[i] = document.getElementById('ctrl_' + i);
      i++;
    } while (audio[i - 1]);
    tracks = i - 1;

    function makeHandler(j) {
      return function() {
        alert(j);
      };
    }
    for (var j = 0; j < tracks; j++) {
      ctrl[j].onclick = makeHandler(j);
    }
button {
  display: block;
}
<button id="ctrl_0">Button 0</button>
<button id="ctrl_1">Button 1</button>
<button id="ctrl_2">Button 2</button>
<button id="ctrl_3">Button 3</button>
<button id="ctrl_4">Button 4</button>
<button id="ctrl_5">Button 5</button>

<div id="track_0">placeholder 0</div>
<div id="track_1">placeholder 1</div>
<div id="track_2">placeholder 2</div>
<div id="track_3">placeholder 3</div>
<div id="track_4">placeholder 4</div>
<div id="track_5">placeholder 5</div>

person Harold Ship    schedule 23.03.2015
comment
Я только что добавил этот обработчик в свой код, и он отлично работает, спасибо! :) Насчет control_ и ctrl_: это опечатка, идентификаторы элементов на самом деле являются control_ в HTML-части кода. Я собираюсь изменить это в ОП. - person vitaminx; 23.03.2015
comment
@vitaminx это здорово! - person Harold Ship; 23.03.2015

ctrl[j].onclick = function () {
    document.writeln(j);
}

что делает этот фрагмент кода, он назначает функцию, которая говорит, что записывает значение j, прослушивателю события onclick элемента

как только цикл for завершится, он присвоит функцию соответствующим элементам, а значение j будет равно количеству элементов на странице.

Когда фактическая функция выполняется. Он печатает значение «j».

Чтобы добиться желаемого результата, вам нужно будет использовать замыкания. взгляните на этот ответ

person Anbarasan    schedule 23.03.2015

Измените цикл for на:

 for (var j = 0; j < tracks; j++) {
    ctrl[j].onclick = function (index) {
        return function(){ alert(index); }
    }(j);
 }

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

person maxeh    schedule 23.03.2015

Другой подход: вы также можете сохранить trackId в атрибуте data самого элемента DOM.

for (var j = 0; j < tracks; j++) {
    ctrl[j].setAttribute("data-trackId", j);
    ctrl[j].onclick = function(event) {
        console.log(event.toElement.getAttribute("data-trackId"));
    }
}
person nuala    schedule 23.03.2015