Как я могу получить объект внутри обработчика событий javascript? - PullRequest
5 голосов
/ 19 января 2011

Я пытаюсь получить объект внутри функции обработчика события onclick.

Но это не работает так, как я ожидаю.

Например, если я запускаю этот код:

var entries = [{id: 1},{id: 2},{id: 3}];

for (var i = 0; i < entries.length; i++) {

    var entry = entries[i];

    document.getElementById(entry.id).onclick = function () { 
        console.log("this.id: " + this.id);
        console.log("entry.id: " + entry.id);
    };

}

То, что я ожидаю, это:

this.id: 1
entry.id: 1

this.id: 2
entry.id: 2

this.id: 3
entry.id: 3

Но я получаю:

this.id: 1
entry.id: 3

this.id: 2
entry.id: 3

this.id: 3
entry.id: 3

Почему входной объект всегда является записью с идентификатором 3?

Как я могу получить правильный объект ввода внутри обработчика события click?

Ответы [ 3 ]

6 голосов
/ 19 января 2011

Один из способов исправить это:

var entries = [{id: 1},{id: 2},{id: 3}];

for (var i = 0; i < entries.length; i++) {

    var entry = entries[i];

    document.getElementById(entry.id).onclick = (function (id) {
        return function () { 
            console.log("this.id: " + this.id);
            console.log("entry.id: " + id);
        };
    })(entry.id);

}

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

Если вы этого не сделаете, все ваши обработчики onclick будут привязаны к одной и той же переменной ввода и в итоге получат последнее значение, полученное во время цикла for.

2 голосов
/ 19 января 2011

Это из-за способа работы замыканий в JavaScript. Ссылка onclick на все 3 элемента хранит ту же функцию, которая после цикла имеет запись, указывающую на последнюю запись. Отсюда и результат. Вы можете получить свой результат, обернув его в другую функцию, имеющую необходимую запись. Вот один из способов сделать это:

var entries = [{id: 1},{id: 2},{id: 3}];

for (var i = 0; i < entries.length; i++) {

    var entry = entries[i];

    function setOnClickHandler(entry) {
        document.getElementById(entry.id).onclick = function () { 
            console.log("this.id: " + this.id);
            console.log("entry.id: " + entry.id);
        };  
    }

    setOnClickHandler(entry);
}

Я написал небольшой пост на этом .

2 голосов
/ 19 января 2011

Это одна из хитрых вещей с закрытием. Вы создаете эти обработчики событий с закрытием более entry. Все они имеют доступ к этой переменной, этой точной переменной. Следовательно, как только эти события срабатывают, они получают только последнее значение, установленное для входа. Вы должны сломать закрытие. Вот один из способов.

function addClickHandlers() {
  var entries = [{id: 1},{id: 2},{id: 3}];
  var i = entries.length;
  while (i--) {
    var entry = entries[i];
    document.getElementById(entry.id).onclick = getClickHandler(entry);
  }
}  

function getClickHandler(entry) {
  return function() { 
    console.log("this.id: " + this.id);
    console.log("entry.id: " + entry.id);
  };
}

Примечание: я изменил ваш цикл for на некоторое время только потому, что это самый быстрый способ сделать цикл в javascript, когда порядок не важен, и я думаю, что он гладкий.

Добавлено для полноты

Есть несколько других методов, разработанных для решения этой ситуации. Я считаю, что я использую их все время.

В Ext есть createDelegate

myhandler.createDelegate(scope, [arguments], [appendArgs]);

Для JavaScript 1.8.5 есть Function.prototype.bind

myhandler.bind(scope, arguments)

Написание Function.prototype.bind довольно просто ( поднято отсюда )

Function.prototype.bind = function(self, var_args) {
  var thisFunc = this;
  var leftArgs = Array.slice(arguments, 1);
  return function(var_args) {
    var args = leftArgs.concat(Array.slice(arguments, 0));
    return thisFunc.apply(self, args);
  };
};  
...