Как назначить обратные вызовы событий, перебирая массив в javascript (jQuery)

Я генерирую неупорядоченный список с помощью javascript (используя jQuery). Каждый элемент списка должен получить свой собственный прослушиватель событий для события «click». Однако у меня возникли проблемы с установкой правильного обратного вызова, прикрепленного к нужному элементу. (Раздетый) пример кода может немного прояснить ситуацию:

for(class_id in classes) {
    callback = function() { this.selectClass(class_id) };
    li_item = jQuery('<li></li>')
                .click(callback);
}

На самом деле, в этой итерации происходит больше, но я не думаю, что это было очень важно для вопроса. В любом случае происходит то, что функция обратного вызова, по-видимому,referenced скорее, чемstored (& скопировано). Конечный результат? Когда пользователь нажимаетany из элементов списка, он всегда будет выполнять действие дляlast class_id вclasses массив, так как он использует функцию, хранящуюся вcallback в этот конкретный момент.

Я нашел грязные обходные пути (например, разборhref атрибут в закрытомa элемент), но мне было интересно, есть ли способ достичь моих целей в «чистой»? путь. Если мой подход ужасен, скажите, пожалуйста, до тех пор, пока вы говорите мне почему :-) Спасибо!

 KnackeredCoder09 июл. 2009 г., 17:04
Как сказал Питер, вы столкнулись с проблемой закрытия. Для получения дополнительной информации об этом K Скотт Аллен имеет достойный пост здесь:odetocode.com/Blogs/scott/archive/2007/07/09/11077.aspx
 geowa409 июл. 2009 г., 17:08
на самом деле, это похоже на простую задачу определения объема. смотрите ответ ниже.

Ответы на вопрос(6)

$("<li />").data("class_id", class_id).click(function(){
    alert("This item has class_id "+$(this).data("class_id"));
});

Будьте осторожны: вы создаете функцию обратного вызова заново для каждого$("<li />") вызов. Я не уверен в деталях реализации JavaScript, но это может быть дорого. Вместо этого вы могли бы сделать

function listItemCallback(){
     alert("This item has class_id "+$(this).data("class_id"));
}

$("<li />").data("class_id", class_id).click(listItemCallback);

for(class_id in classes) {
    callback = function() { this.selectClass(class_id) };
    li_item = jQuery('<li></li>')
                        .click(callback);
}

Это в основном нормально, только одна проблема. Переменнаяcallback является глобальным; поэтому каждый раз, когда вы зацикливаетесь, вы перезаписываете его. Положитьvar Ключевое слово перед ним, чтобы охватить его локально, и вы должны быть в порядке.

РЕДАКТИРОВАТЬ для комментариев: Это может быть не глобальным, как вы говорите, но это выходит за рамки цикла for. Таким образом, переменная является одной и той же ссылкой каждый раз в цикле. Вводvar в цикле ограничивает его до цикла, каждый раз делая новую ссылку.

 09 июл. 2009 г., 20:07
меньше событий == лучше
 JorenB09 июл. 2009 г., 17:34
Здорово. Я думаю, что это будет решением при использовании моего подхода. Тем не менее, live () - way тоже отлично работает. Есть ли такая вещь, как «лучше»? один из двух?
 JorenB09 июл. 2009 г., 17:12
Прямо сейчас он инициализирован. непосредственно перед циклом, используяvar ключевое слово. Вы имеете в виду, что это будет работать нормально, если я положу этоvarключевое слово внутри цикла?
 09 июл. 2009 г., 17:17
довольно много. он может быть не глобальным, как вы говорите, но он выходит за рамки цикла for. поэтому переменная является одной и той же ссылкой каждый раз в цикле. положить варin цикл ограничивает его циклом, каждый раз делая новую ссылку.

а затем вызвать что-то вроде

$(".li_class").click(function(){ this.whatever() };

РЕДАКТИРОВАТЬ:

Если вам нужно добавить больше классов, просто создайте в цикле строку со всеми именами классов и используйте ее как селектор r.

$(".li_class1, .li_class2, etc").click(function(){ this.whatever() };
 JorenB09 июл. 2009 г., 16:50
Хм ... мне нужно "прикрепить" разные class_id для каждого элемента списка. Я упускаю простой способ сделать это, используя ваш подход?
 09 июл. 2009 г., 17:00
Вы прикрепляетеsame класс каждомуli. this будет конкретный элемент, который был нажат
 09 июл. 2009 г., 17:01
единственная проблема, которую я имею, состоит в том, чтобы повторить дважды по тому же набору. один раз создать, снова добавить обработчик.

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

function createClosure(class_id) {
  callback = function() { this.selectClass(class_id) };
  return callback;
}

а потом:

for(class_id in classes) {
  callback = createClosure(class_id);
  li_item = jQuery('<li></li>').click(callback);
}

Это, конечно, немного круто, возможно, есть и лучшие способы.

 JorenB09 июл. 2009 г., 17:02
Выглядит многообещающе ... Мне, вероятно, придется двигаться куда-то в этом направлении, хотя, как вы сказали, он все еще "чувствует" немного странно ...
Решение Вопроса

Добавьте информацию class_id в элемент, используя .data ().

Затем используйте .live (), чтобы добавить обработчик кликов ко всем новым элементам, чтобы избежать использования функций x * click.

for(class_id in classes) {
    li_item = jQuery('<li></li>').data('class_id', class_id).addClass('someClass');
}

//setup click handler on new li's
$('li.someClass').live('click', myFunction )

function myFunction(){
   //get class_id
   var classId = $(this).data('class_id');
   //do something
}
 JorenB09 июл. 2009 г., 17:27
Милая. Это на самом деле работает супер удивительным образом. Спасибо, что указал мне на это. Теперь: кажется, что он принимает только jQuery (cssselector). Если я использую list.children (). Live (blabla), в котором «список» является объектом jQuery для & lt; ul & gt ;, он не будет работать. Для этого есть причина?
 09 июл. 2009 г., 20:25
не пробовал это с .children (), но я думаю, что ему нужен фактический селектор, поскольку он использует делегирование события в документе, а затем проверяет, соответствует ли селектор событию. Вы можете вернуться к использованию обычного делегирования события на ул.

анчивается.

Iterate over some values Define/assign a function in that iteration that uses iterated variables You learn that every function uses only values from the last iteration. WTF?

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

Расширяя свой пример, вот как вы положили конец

for ( class_id in classes )
{
  callback = function( cid )
  {
    return function()
    {
      $(this).selectClass( cid );
    }
  }( class_id );
  li_item = jQuery('<li></li>').click(callback);
}

Однако в этом конкретном случае jQuery вам не нужно закрытие - но мне нужно спросить о природе вашей переменнойclasses - это объект? Потому что вы перебираете цикл for-in, который предлагает объект. И для меня напрашивается вопрос, почему вы не храните это в массиве? Потому что, если бы вы были, ваш код мог бы быть таким.

jQuery('<li></li>').click(function()
{
  $(this).addClass( classes.join( ' ' ) );
});

Ваш ответ на вопрос