Обработчик событий jQuery, созданный в цикле

Итак, у меня есть группа таких событий:

$('#slider-1').click(function(event){   
        switchBanners(1, true);
});
$('#slider-2').click(function(event){   
        switchBanners(2, true);
});
$('#slider-3').click(function(event){   
        switchBanners(3, true);
});
$('#slider-4').click(function(event){   
        switchBanners(4, true);
});
$('#slider-5').click(function(event){   
        switchBanners(5, true);
});

И я хотел запустить их через цикл. Я уже запускаю что-то вроде этого:

for(i = 1; i <= totalBanners; i++){
    $('#slider-' + i).click(function(event){    
            switchBanners(i, true);
    });
}   

В теории, которая должна работать, но не кажется, что когда-то я загружаю документ... Он не реагирует на какой-либо конкретный идентификатор div, как он должен при нажатии... он проходит через каждый div, независимо от того, на какой из них я нажимаю. Есть больше слушателей событий, которые я хочу динамически создавать "на лету", но мне нужны эти первые...

Что мне не хватает?

Ответ 1

Это очень распространенная проблема, с которой сталкиваются люди.

JavaScript не имеет области действия блока, а просто области функции. Поэтому каждая функция, которую вы создаете в цикле, создается в одной и той же среде переменных, и поэтому они все ссылаются на ту же переменную i.

Чтобы охватить переменную в новой среде переменных, вам нужно вызвать функцию, которая имеет переменную (или параметр функции), которая ссылается на значение, которое вы хотите сохранить.

В приведенном ниже коде мы ссылаемся на него с параметром функции j.

   // Invoke generate_handler() during the loop. It will return a function that
   //                                          has access to its vars/params. 
function generate_handler( j ) {
    return function(event) { 
        switchBanners(j, true);
    };
}
for(var i = 1; i <= totalBanners; i++){
   $('#slider-' + i).click( generate_handler( i ) );
}

Здесь мы вызывали функцию generate_handler(), переданную в i, и имели generate_handler() возвращали функцию, которая ссылается на локальную переменную (с именем j в функции, хотя вы могли бы назвать ее i).

Переменная среда возвращаемой функции будет существовать до тех пор, пока существует функция, поэтому она будет продолжать ссылаться на любые переменные, существовавшие в среде, когда/где она была создана.


UPDATE: Добавлен var до i, чтобы убедиться, что он правильно объявлен.

Ответ 2

Вместо того, чтобы что-то делать, это.. emm.. безрассудно, вы должны приложить одного прослушивателя событий и поймать события, которые они пузырятся. Его называют "делегированием событий".

Некоторые ссылки:

Изучите это. Очень важно узнать об управлении событиями в javascript.

Ответ 3

[edit: увидел, что этот ответ получил верхний знак и распознал его с использованием старого синтаксиса. Вот несколько обновленных синтаксисов, используя метод привязки событий jQuery "on". Тот же принцип применяется. Вы связываетесь с ближайшим неиспользуемым родителем, слушая клики на указанном селекторе.]

$(function() {
  $('.someAncestor').on('click', '.slider', function(e) {
    // code to do stuff on clicking the slider. 'e' passed in is the event
  });
});

Примечание. Если ваша цепочка инициализации уже имеет подходящее место для вставки слушателя (то есть у вас уже есть готовая документа или функция загрузки), вам не нужно обертывать ее в этом примере $(function(){}). Вы просто поместите часть $('.someAncestor')... в соответствующее место.

Оригинальный ответ поддерживается для более подробного объяснения и старого образца кода:


Я с tereško: делегирование событий является более мощным, чем выполнение каждого щелчка "по требованию". Самый простой способ получить доступ ко всей группе элементов слайдера - дать каждому общий класс. Скажем, "слайдер". Затем вы можете делегировать универсальное событие всем элементам ".slider":

$(function() {
    $('body').delegate('.slider', 'click', function() {
        var sliderSplit = this.id.split('-'); // split the string at the hyphen
        switchBanners(parseInt(sliderSplit[1]), true); // after the split, the number is found in index 1
    });
});

Liddle Fiddle: http://jsfiddle.net/2KrEk/

Я делегирую "тело" только потому, что не знаю вашей структуры HTML. В идеале вы делегируете ближайший родитель всех слайдеров, которые, как вы знаете, не будут уничтожены другими манипуляторами DOM. Часто ome вроде обертки или контейнера div.

Ответ 4

Это потому, что i не оценивается до тех пор, пока не будет вызвана функция click, и к этому времени цикл завершится, а i будет у него max (или, что еще хуже, переписан где-то еще в коде).

Попробуйте следующее:

for(i = 1; i <= totalBanners; i++){
    $('#slider-' + i).click(function(event){    
        switchBanners($(this).attr('id').replace('slider-', ''), true);
    });
}   

Таким образом вы получаете номер из идентификатора элемента, который был нажат.