Solo una pregunta rápida, ¿alguien puede decirme por qué esto no funciona y cómo solucionarlo? Esencialmente es tomar un grupo de filas de tabla en HTML y adjuntarles dinámicamente eventos de clic.

for (var a=index; a<rows.length; a++) {
    tr = rows[a];
    tr.onclick = function() { DropDownManager.onItemClick(tr, a); };
    tr.observe("click", function() { DropDownManager.onItemClick(tr, a); });
}

El problema con este código es que los valores pasados a DropDownManager.onItemClick son siempre los últimos elementos en el bucle, esto no es lo que quiero después, ya que quería que fueran el valor actual en esa etapa del bucle. Me doy cuenta de que me estoy perdiendo algo bastante simple, ¡pero no puedo resolverlo!

8
Ian Harrigan 6 ago. 2011 a las 20:28

4 respuestas

La mejor respuesta

JavaScript no tiene alcance de bloque, por ejemplo los bucles no crean un nuevo alcance. Puede crear uno utilizando una función:

for (var a=index; a<rows.length; a++) {
   (function(a) {
      tr = rows[a];
      tr.onclick = function() { DropDownManager.onItemClick(this, a); };
      tr.observe("click", function() { DropDownManager.onItemClick(this, a); });
   }(a));
}

Dependiendo de lo que sean rows y tr, es posible que ni siquiera necesite el índice de bucle. Tal vez pueda obtener el índice de elementos dentro del controlador de eventos de otra manera. P.ej. si tr es un HTMLTableRowElement [MDN] , entonces puede obtener su posición entre las otras filas a través de this.rowIndex.

Por cierto. ¿Por qué vincula el mismo controlador de eventos dos veces?

14
sschuberth 11 may. 2015 a las 13:06

Además de almacenar los atributos en el objeto DOM en el bucle, también puede usar los cierres de funciones para "congelar" una copia de las variables en el bucle para una llamada de función particular. Puedes hacerlo así:

for (var a=index; a<rows.length; a++) {
    tr = rows[a];
    tr.onclick = function(tr, a) {
        return(function() { 
            DropDownManager.onItemClick(tr, a); 
        });
    }(tr,a);
}

Lo que esto hace es asignarle a tr.onclick los resultados de ejecutar una llamada de función anónima que toma dos variables como parámetros (llamados tr y a) y pasa los valores actuales de tr y a como parámetros (este paso de los valores actuales " congela "los valores actuales de esas variables dentro del cierre de esta función.

El resultado de ejecutar esa llamada de función es en sí otra función anónima. Debido a que esta función anónima interna ahora está asignada a tr.onclick, crea un cierre de función que mantiene vivo todo el estado que se encuentra actualmente en ese cierre. Ese estado incluye los valores "congelados" de tr y a, pero se mantienen internos solo para este cierre de función, por lo que cada vez que se realiza el bucle se crea un nuevo cierre de función con un nuevo valor "congelado" y "almacenado" de tr y a.

4
jfriend00 6 ago. 2011 a las 16:59

En parte porque soy pedante y en parte porque es posible, aquí se hace lo mismo de manera simple.

Event.on('table_id', 'click', 'tr', function(event, tr) {
    DropDownManager.onItemClick(tr, rows.indexOf(tr));
});

Observe que la tabla que contiene es necesaria para que los eventos surjan, se identifica por su ID, pero también puede pasar el elemento directamente.

0
clockworkgeek 6 ago. 2011 a las 17:13
for (var a=index; a<rows.length; a++) {
    tr = rows[a];
    tr.setAttribute('a', a);
    tr.onclick = function() { DropDownManager.onItemClick(this, this.getAttribute('a')); };
    tr.observe("click", function() { DropDownManager.onItemClick(this, this.getAttribute('a')); });
}

Intenta de esta manera.

Pasará tr porque la variable tr se establece como una última fila.

-1
Senad Meškin 6 ago. 2011 a las 16:31