Alters

Tips & Tricks: Overlay de Eventos en JavaScript

Buenas!

Se avecinan cambios por aquí... de momento todo queda tal como está, pero nunca se sabe...

En fin, vamos a ver un poco el tema del solapamiento de eventos en JavaScript.

Y es que mucho se ha de tener en cuenta a la hora de hacer una serie de funciones JavaScript, ya que, en ocasiones, puede que no suceda aquello que esperamos...

Y es que muchas veces lo que esperamos no es exactamente lo mismo que el navegador "interpreta"... y uno de estos casos es el solapamiento.

Hay varios tipos de solapamiento en JavaScript (realmente se le llama "solapamiento"?). Empezaremos con los básicos, y luego iremos con el tema de AJAX (JavaScript y XML Asíncronos).

¿Qué es el solapamiento?

Supongamos que tenemos esta estructura HTML:

<body>
  <div id="contenido">¡Haz Click!</div>
  <div id="contenedor"></div>
</body>

Y supongamos que queremos que al hacer click sobre el texto (el div, en realidad) se muestre un texto en "contenedor".

Podríamos hacer algo así:

<body>
  <div id="contenido" onclick="document.getElementById('contenedor').innerHTML = 'contenido';>¡Haz Click!</div>
  <div id="contenedor"></div>
</body>


Hay muchas maneras para hacer lo que queremos; podríamos, por ejemplo, crear un nuevo "div"  desde cero (de "la nada") y ponerle el texto, pero esto (me da la sensación) que sería más costoso.

Así, lo que hacemos en el código de arriba es añadir un handler (manejador) que saltará cuando hagamos click (en inglés "on click", y todo junto "onclick") es editar el html interno del elemento con id "contenedor".

Ahora vamos a dejarlo todo por separado, el JS por un sitio y el HTML por otro:

[div]
<script type="text/javascript">
function muestra(){
 document.getElementById('contenedor').innerHTML = 'contenido';
 return false;
}
</script>
<body>
  <div id="contenido" onclick="muestra();">¡Haz Click!</div>
  <div id="contenedor" ></div>
</body>


Esta función no tiene mucho sentido, pues es muy "particular". Así que la vamos a hacer de ámbito general para poder usarla por más de un elemento:

function muestra(elemento,texto){
  document.getElementById(elemento).innerHTML = texto;
  return false;
}

Bien, ahora nuestro handler sería:

onclick="muestra('contenedor', 'Esto es texto');"

Ahora podríamos hacer que cualquier "div" mostrase cualquier texto... es más general.

Sigamos haciendo nuestra "web"... ahora queremos que al pulsar otro elemento que no sea el div "contenido" el texto se oculte.

Para denotar "cualquier elemento", podemos usar el body mismo... ya que éste contiene todos los elementos.

<body onclick="document.getElementById('contenedor').innerHTML = '';"> ** nótese que son dos comillas simples, un ";" y unas comillas dobles **

o lo que es lo mismo:

<body onclick="muestra('contenedor', '');">

Y vamos a probar, y... ¡OH! nuestra "web" no funciona!

Como decía antes, sí funciona, solo que no como esperábamos.

¿Y qué ha pasado? Se han solapado dos eventos JavaScript.

Vamos a explicar esto un poco: imaginad una web como algo tridimensional... (como lo hace FireFox cuando editamos el diseño OTF :-P )

Entonces, nuestra "web" sería algo así (lo haría más bien hecho, pero es tarde y no me apetece mucho jugar con las perspectivas del CorelDraw X5...):



Supongamos que el cubo verde es "contenido", el rojo "contenedor" y lo azul es "body". Al hacer click en "contenido" saltará el evento que pone el texto, pero a su vez estamos haciendo click sobre "body", por lo que vuelve a saltar el evento, borrando el texto...

Es decir, así como los programas tienen una pila de llamadas donde se apilan las llamadas de funciones, para saber a qué dirección de memoria volver cuando acabe la función actual, JS tiene una pila eventos, de manera que parece que no funcione, pero en realidad va todo tan rápido que parece que no funcione...

Entonces, ¿Cómo hacemos para que funcione como queremos?

Analicemos la situación: al hacer click en "contenido", la función es llamada dos veces, mientras que en cualquier otro sitio solo es llamada una vez.

Por tanto, podemos definir las siguientes cláusulas:

 - Si la función se ha de mantener, se harán X clicks (2, en este caso)
 - Si no, se harán solo Y clicks (1 en este caso).

Bien, vamos a hacer un poco más grande nuestro JS:

<script type="text/javascript">
var max = 2;

function muestra(elemento, contenido, flag){
  max--;

  if(flag && max > 0){
   document.getElementById(elemento).innerHTML = '';
  }else{
    if(!flag){
        document.getElementById(elemento).innerHTML = contenido;
    }
  }

  if(max == 0 || flag){
    max = 2;
  }
}
</script>

Entonces, vamos a ver:

Primero hacemos un contador global, con valor 2. Luego hemos modificado la función para que acepte un parámetro adicional (booleano), para diferenciar la llamada del div de otras.

Entonces, en "contenido" haremos:

onclick="muestra('contenedor', 'texto', false);"

y en body:

onclick="muestra('contenedor', 'texto', true);"

Así, al hacer click sobre el div, pasará lo siguiente (suponiendo una primera ejecución):


  • Parámetros de entrada: "contenedor", "texto", "false"
  • max disminuye (max = 1)
  • Entra en el "else"
  • Se escribe el texto
  • No entra en el if
  • Devuelve la llamada a la pila
  • Llamada desde Body: "contenedor", "", "true"
  • max disminuye (max = 0)
  • Entra en el else
  • NO entra en el if (y por tanto no se modifica nada)
  • Entra en el segundo if (max = 2)
¡Bien!, el texto ya se queda. Si llamamos directamente desde body:

  • Parámetros: "contenedor", "texto", "true"
  • max disminuye (max = 1)
  • Entra en el if
  • Vacía el elemento
  • Entra en el segundo if (max = 2)
Y hemos evitado que, al solaparse, se produzca un borrado.

En la próxima entrada, veremos solapamientos con AJAX, y cómo solucionarlos de varias maneras.

Más adelante (la siguiente entrada a la de AJAX), entraremos en un mundo algo diferente: Matemáticas.

Ya veremos cómo acaba esta aventura.

Saludos, como siempre, y...

¡Hasta la próxima!