Почему я не могу добавить событие к каждому элементу в коллекции, которое ссылается на себя, а не на последний элемент в выражении "for () {}" - PullRequest
3 голосов
/ 08 апреля 2010

При загрузке Window каждый DD элемент внутри Quote_App должен иметь добавленное событие onCLick, которое вызывает функцию Lorem, однако Lorem возвращает nodeName и Id последнего элемента в операторе For, а не элемента, который вызвал функцию. Я бы хотел, чтобы Lorem вернул nodeName и Id элемента, который вызвал функцию.

function Lorem(Control){

/*     this.Control=Control; */
    this.Amet=function(){
        return Control.nodeName+"\n"+Control.id;
    };

};

function Event(Mode,Function,Event,Element,Capture_or_Bubble){
    if(Mode.toLowerCase()!="remove"){
        if(Element.addEventListener){
            if(!Capture_or_Bubble){
                Capture_or_Bubble=false;
            }else{
                if(Capture_or_Bubble.toLowerCase()!="true"){
                    Capture_or_Bubble=false;
                }else{
                    Capture_or_Bubble=true;
                };
            };
            Element.addEventListener(Event,Function,Capture_or_Bubble);
        }else{
            Element.attachEvent("on"+Event,Function);
        };
    };
};

function Controls(){
    var Controls=document.getElementById("Quote_App").getElementsByTagName("dd");
    for(var i=0;i<Controls.length;i++){

        var Control=Controls[i];

        Event("add",function(){

            var lorem=new Lorem(Control);
            lorem.Control=Control;
            alert(lorem.Amet());

        },"click",Controls[i]);

    };
};

Event("add",Controls,"load",window);

В настоящее время вы нажимаете на любой элемент DD. Lorem всегда возвращает nodeName и Id последнего элемента DD.

Lorem должен вернуть nodeName и Id из Control (Control[i]), которые сработали Lorem.

Как мне это сделать?

Спасибо!

Ответы [ 3 ]

5 голосов
/ 08 апреля 2010

вам нужно замыкание внутри цикла, где вы присоединяете обработчики событий, чтобы захватить значение i в каждой итерации цикла.

  for(var i=0;i<Controls.length;i++) {   
    (function() {
        var Control=Controls[i];

        Event("add",function(){

            var lorem=new Lorem(Control);
            lorem.Control=Control;
            alert(lorem.Amet());

         },"click",Controls[i]);
    })();
  };

Здесь я создал замыкание выше, используя хорошего друга JavaScript, самовозглашающуюся анонимную функцию.

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

Еще один момент, немного не по теме, но он может вам помочь, это то, что захват событий поддерживается не во всех браузерах ( ahem , IE), но всплывающее окно событий. Это фактически делает логический флаг useCapture в addEventListener совершенно бесполезным для разработки кросс-браузерных веб-приложений. Поэтому я бы посоветовал не использовать его.

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

3 голосов
/ 08 апреля 2010

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

В вашем случае:

var functions = [];
function outer() {
    for (var i = 0; i < N; i++) { // <------------
        functions[i] = function() { //            |
            alert(i); // <-- this 'i' refers to this one
        }
    } // At the end of the for loop, i == N (or N+1?)
}
functions[x](); // Will show N (or N+1)
// because that's the current value of i in the outer function
// (which is kept alive just because something refers to it)

То, что вы хотите сделать, это захватить значение 'i' на каждом шаге цикла, для последующей оценки, например ::

var functions = [];
function outer() {
    for (var i = 0; i < N; i++) { // <---------------------------------------
        functions[i] = (function(my_i) { // <----                            |
            return function () { //              |                           |
                alert(my_i); // my_i refers to this which is a copy of 'i' there
            }
        }(i)); // run function(with my_i = the i at each step of the loop)
    }
}
functions[x](); // Will show x

Вы можете видеть, что есть внутренняя функция, которая получает копию текущего счетчика в качестве параметра. Эта внутренняя функция остается в живых с этой копией, поэтому последующие вызовы сохраненной функции innest возвращают значение my_i функции, которая ее создала - Clear? : -)

Это чудесный мир замыканий. Сначала может потребоваться немного сумасшедшего, чтобы получить его, но потом вы будете рады, что сделали это, поэтому отправляйтесь и погуглите «закрытие javascript» до смерти!

2 голосов
/ 08 апреля 2010

Это может быть более очевидный вариант ответа Расса Кэма:

function Controls() {
  var Controls = document.getElementById("Quote_App").getElementsByTagName("dd");

  var createHandlerFor = function(CurrentControl) {
    return function() {
      var lorem=new Lorem(CurrentControl);
      lorem.Control=CurrentControl;
      alert(lorem.Amet());
    }
  };

  for (var i=0; i<Controls.length; i++) {
    Event("add", createHandlerFor(Controls[i]), "click", Controls[i]);
  }
};
...