Foros del Web » Programando para Internet » Javascript »

Problema con addEventListener

Estas en el tema de Problema con addEventListener en el foro de Javascript en Foros del Web. Hola. Estoy empezando a interiorizarme con javascript. Tengo un problema. Según tengo entendido addEventListener permite que se ejecute la función indicada en el 2º parámetro, ...
  #1 (permalink)  
Antiguo 20/12/2011, 12:41
 
Fecha de Ingreso: noviembre-2009
Mensajes: 15
Antigüedad: 14 años, 6 meses
Puntos: 0
Exclamación Problema con addEventListener

Hola. Estoy empezando a interiorizarme con javascript. Tengo un problema.

Según tengo entendido addEventListener permite que se ejecute la función indicada en el 2º parámetro, cuando ocurra el evento especificado en el 1º parámetro.

Tengo una página html que tiene una lista UL con id 'gal' con cuatro elementos LI.

Con un script, genero otra lista encima de la anterior cuyo id es 'nav' con cuatro elementos LI, cada uno de los cuales tienen un único elemento A. La función de esta lista generada sería la de menu de navegación.

Luego, agrego un addEventListener para cada elemento A de la lista generada para que al hacer click en ellos se ejecute una función que muestre solo un elemento de la lista original, mientras que oculte los otros (display:none).

El problema es que la función especificada en addEventListener no se ejecuta al hacer click en un elemento A, sino que se ejecuta apenas se carga la página

Aquí está el script:

Código HTML:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
  <meta http-equiv="content-type" content="text/html; charset=windows-1250">
  <title></title>
  <script type = 'text/javascript'>

    window.onload = function() {
      function  muestraOculta($idEl)
      {
        var $f = 0;
        while ( $f < $lis.length )
        {
          if ( $lis[$f].id == 'pag' + $idEl )
          {
            $lis[$f].style.display = 'list';
          }
          else
          {
            $lis[$f].style.display = 'none';
          };
          $f++;
        };
      }
      function crearNav()
      {
        var $newUl = document.createElement('ul');
        $newUl.setAttribute("id","nav");
        for ( var $i = 0 ; $i < $lis.length ; $i++ )
        {
          var $newLi = document.createElement('li');
          var $newA = document.createElement('a');
          var $newT = document.createTextNode($i+1);
          $newA.setAttribute("href","#pag"+($i+1));
          $newA.setAttribute("id",$i+1);
          $newA.appendChild($newT);
          $newLi.appendChild($newA);
          $newUl.appendChild($newLi);
          //$newA.addEventListener('click', muestraOculta($newA.id), false);
        };
        document.body.insertBefore($newUl, $gal);
        $links = document.getElementById('nav').getElementsByTagName('a');
        for ( var $k = 0 ; $k < $links.length ; $k++ )
        {
          $links[$k].addEventListener('click', muestraOculta($links[$k].id), false);
        };
      };
      var $gal = document.getElementById('galeria');
      var $lis = $gal.getElementsByTagName('li');
      crearNav();
    };

  </script>
</head>
  <body>
    <h1>Galería de imágenes (sin imágenes)</h1>
    <ul id = "galeria">
      <li id = "pag1">
        <h2>Primera página</h2>
        <p>Este es el párrafo presente en la <strong>primera</strong> página.</p>
      </li>
      <li id = "pag2">
        <h2>Segunda página</h2>
        <p>Este es el párrafo presente en la <strong>segunda</strong> página.</p>
      </li>
      <li id = "pag3">
        <h2>Tercera página</h2>
        <p>Este es el párrafo presente en la <strong>tercera</strong> página.</p>
      </li>
      <li id = "pag4">
        <h2>Cuarta página</h2>
        <p>Este es el párrafo presente en la <strong>cuarta</strong> página.</p>
      </li>
    </ul>

  </body>
</html> 
Gracias por su ayuda.

Pd: Estoy utilizando Chrome

Última edición por Dago1988; 20/12/2011 a las 12:49 Razón: Faltas de ortografía, modificar código
  #2 (permalink)  
Antiguo 20/12/2011, 12:55
Avatar de zerokilled
Javascripter
 
Fecha de Ingreso: abril-2009
Ubicación: Isla del Encanto, La Borinqueña [+>==]
Mensajes: 8.050
Antigüedad: 15 años
Puntos: 1485
Respuesta: Problema con addEventListener

buenas,
se ejecuta porque esta invocando la función. recuerda que un par de parentesis seguido del nombre de la función significa invocación. en este caso, lo que estas asignando como handler del evento es lo que devuelve la función invocada. fijate en lo marcado en negrita.
Código:
$links[$k].addEventListener('click', muestraOculta($links[$k].id), false);
en el segundo parámetro tienes que pasar una función. es decir, en lugar de muestraOculta($links[$k].id), debes pasar solo el nombre: muestraOculta. si la función requiere de parámetros puedes pasarle una función anónima que invoca función con los parámetros necesarios. por ejemplo,
Código:
function(){
// asumiendo que el nombre de la funcion es function_call;
function_call(params);
}
__________________
la maldad es una virtud humana,
y la espiritualidad es la lucha del hombre contra su maldad.
  #3 (permalink)  
Antiguo 20/12/2011, 13:15
 
Fecha de Ingreso: noviembre-2009
Mensajes: 15
Antigüedad: 14 años, 6 meses
Puntos: 0
Exclamación Respuesta: Problema con addEventListener

Gracias por responder.
¿Así?
Código:
        for ( var $k = 0 ; $k < $links.length ; $k++ )
        {
          alert($links[$k].id)
          $links[$k].addEventListener('click', function(){muestraOculta($links[$k].id);}, false);
        };
La consola de javascript da este error al hacer click en los elementos A, refiriendose al elemento $links[$k].id :
Cita:
46Uncaught TypeError: Cannot read property 'id' of undefined
Gracias por la ayuda.

Última edición por Dago1988; 20/12/2011 a las 13:17 Razón: agregar información
  #4 (permalink)  
Antiguo 20/12/2011, 14:09
 
Fecha de Ingreso: noviembre-2009
Mensajes: 15
Antigüedad: 14 años, 6 meses
Puntos: 0
De acuerdo Respuesta: Problema con addEventListener

Ya lo solucioné, gracias zerokilled por la ayuda.
Esto que sigue no funciona porque según pude averiguar, al pasar parámetros para una función usada en addEventListener, estos deben ser variables globales, por lo que no se puede pasar parámetros generados por el bucle en una función (ya que son variables locales).
Código:
        for ( var $k = 0 ; $k < $links.length ; $k++ )
        {
          alert($links[$k].id)
          $links[$k].addEventListener('click', function(){muestraOculta($links[$k].id);}, false);
        };
Hice así y me funcionó (almenos en Chrome):


Código:
        for ( var $k = 0 ; $k < $links.length ; $k++ )
        {
          $links[$k].addEventListener('click', function(){muestraOculta(this.id);}, false);
        };
En vez de usar la variable del bucle for utilizé la variable this que, creo, representa al elemento que desencadenó el evento.
Gracias y saludos.
  #5 (permalink)  
Antiguo 20/12/2011, 14:43
Avatar de zerokilled
Javascripter
 
Fecha de Ingreso: abril-2009
Ubicación: Isla del Encanto, La Borinqueña [+>==]
Mensajes: 8.050
Antigüedad: 15 años
Puntos: 1485
Respuesta: Problema con addEventListener

Cita:
Esto que sigue no funciona porque según pude averiguar, al pasar parámetros para una función usada en addEventListener, estos deben ser variables globales, por lo que no se puede pasar parámetros generados por el bucle en una función (ya que son variables locales).
el problema se debe a otro factor. no lo había previsto porque no había analizado todo el código. no es porque las variables sean locales, que de hecho el evento las ve porque la función anónima esta declarada en el mismo scope donde estan las variables. lo que significa que la función anónima puede acceder a ella. el problema fue que al momento de crear el evento, la función no retiene el valor de la variable de esa iteración. es decir, en este caso particular, la variable $k es el iterador del bucle for. como el valor de $k es mutado, al iniciarse el evento la función lo que ve es el último valor asignado a $k. por tanto, todos los eventos creados en el bucle for accesan al mismo elemento de la colección $links cuando el evento se dispara.

soluciones, para tu caso particular hay varias. una de ella es la delegación de evento. consiste en registrar un solo evento para todos los elementos de interes. si interesa, te recomiendo buscar sobre el tema. busca por palabras claves como javascript delegacion evento. gestion de eventos vs delegacion de evento

otra solución, consiste en crear closure para que el valor de la variable se retenga. la posibilidad se da gracias a que el valor iterable se pasa a un scope distinto donde cada evento tendrá su propio scope. en el siguiente artículo, lee la sección "the infamous loop problem", explaining javascript scope and closures. te lo recomiendo ya que este refleja el problema que estabas teniendo.

finalmente, tu solución es otra posibilidad. eventualmente, this hace referencia al elemento donde se ha asignado el evento. sin embargo, tengo entendido que según el estandar no debería ser así. si bien recuerdo, this dentro de addEventListener debería hacer referencia a otro objeto. en todo caso, no tengo la información a la mano. lo cierto es que todos los navegadores -al menos en lo que he probado- this hace referencia al elemento. por lo que tu solución no esta mal, y de hecho, de todas para mi sería la más sencilla de implementar.

valga todo este rollo para que puedas comprender el problema inicial y algunas de las posibles soluciones. me dejo en el tintero otra posibilidad porque creo que ya es mucho.
__________________
la maldad es una virtud humana,
y la espiritualidad es la lucha del hombre contra su maldad.

Última edición por zerokilled; 20/12/2011 a las 14:49
  #6 (permalink)  
Antiguo 20/12/2011, 17:59
 
Fecha de Ingreso: noviembre-2009
Mensajes: 15
Antigüedad: 14 años, 6 meses
Puntos: 0
De acuerdo Respuesta: Problema con addEventListener

Completísima respuesta, gracias por la clase gratis .
Si, por supuesto que me interesa. Voy a releer tu respuesta y los enlaces que dejaste.
Saludos y suerte.

Etiquetas: addeventlistener
Atención: Estás leyendo un tema que no tiene actividad desde hace más de 6 MESES, te recomendamos abrir un Nuevo tema en lugar de responder al actual.
Respuesta




La zona horaria es GMT -6. Ahora son las 18:03.