Precarga de imágenes

Una precarga de imágenes es algo bastante fácil de implementar, pero ¿realmente es necesario?

El código para nuestro cometido es bastante sencillo, tan solo debemos crear un objeto "Image()" por cada una de las imágenes que queremos precargar; y si quisiéramos llevar un control sobre el estado de las mismas, deberíamos consultar su atributo "complete" o programar el manejador del evento de carga (onload). Esta última opción es útil si queremos evitar que se muestren imágenes que no se hayan cargado.

Código reducido

Vamos a proponer un código básico para una precarga en donde lo único que haremos será crear un objeto Image() por cada imagen que vamos a mostrar.

function precargar()	{
	imagenes = new Array();
	for (i = 0, datos = precargar.arguments, total = datos.length; i < total; i ++)	{
		imagenes[i] = new Image();
		imagenes[i].src = datos[i];
	}
}
window.onload = function()	{
	precargar(
		'imagen.php?id=23',
		'imagen.php?id=24',
		'imagen.php?id=25',
		'imagen.php?id=26',
		'imagen.php?id=27'
		);
}

Este fragmento de código es suficiente para un bloque como el que sigue a continuación.

<div>
	<img src='imagen.php?id=24' alt='Carnaval Almería' id='imagen1' />
</div>
<div>
	<img src='imagen.php?id=25' alt='Castillo Almería' id='imagen2' />
</div>
<div>
	<img src='imagen.php?id=26' alt='Costa del Sol' id='imagen3' />
</div>
<div>
	<img src='imagen.php?id=27' alt='Salobreña' id='imagen4' />
</div>
<div>
	<img src='imagen.php?id=23' alt='Almuñecar' id='imagen5' />
</div>

Estudiando el caso, la precarga se hace pero sin ningún sentido. Cuando se implementa este tipo de código, se suele tener alguna intención distinta a la de mostrar la imagen, y la más corriente es para mostrar imágenes que en principio están ocultas, como los efectos rollover o las presentaciones (slideshows).

Supongamos que tengamos la página oculta tras una capa opaca (o traslúcida si se quiere), y queremos que mientras no se haya cargado se muestre un relojito indicando que debemos esperar, y que se oculte esa capa (o desaparezca) cuando las imágenes estén completamente cargadas.

Primera ampliación: ocultar/mostrar

Ahora debemos llevar el control del número de imágenes para precargar, y programar el evento de la "carga completa", para ello hemos de crear una variable que nos indique el estado de carga, y un método (función) que se active en el momento justo.

Un ejemplo completo podemos ver en el siguiente código:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>
	http://www.caricatos.net/probador
</title>
<style type="text/css" >
#ocultando	{
	position: absolute;
	top: 0;
	left: 0;
	width: 100%;
	height: 100%;
	background: white url(../dibujos/reloj.gif) no-repeat center center;
}
</style>

<script type="text/javascript" >
<!--
function tag(id)	{return document.getElementById(id);}
var cargadas = function()	{
	tag("ocultando").style.visibility = "hidden";
}
var precargadas;
function precargar()	{
	imagenes = new Array();
	precargadas = precargar.arguments.length;
	for (i = 0, datos = precargar.arguments, total = precargadas; i < total; i ++)	{
		imagenes[i] = new Image();
		imagenes[i].onload = function()	{
			if (--precargadas == 0)	cargadas();
		}
		imagenes[i].src = datos[i];
	}
}
window.onload = function()	{
	precargar(
		'imagen.php?id=23',
		'imagen.php?id=24',
		'imagen.php?id=25',
		'imagen.php?id=26',
		'imagen.php?id=27'
		);
}
//-->
</script>
<head>
<body>
<div>
	<img src='imagen.php?id=24' alt='Carnaval Almería' id='imagen1' />
</div>
<div>
	<img src='imagen.php?id=25' alt='Castillo Almería' id='imagen2' />
</div>
<div>
	<img src='imagen.php?id=26' alt='Costa del Sol' id='imagen3' />
</div>
<div>
	<img src='imagen.php?id=27' alt='Salobreña' id='imagen4' />
</div>
<div>
	<img src='imagen.php?id=23' alt='Almuñecar' id='imagen5' />
</div>
<div id="ocultando" ></div>
</body>
</html>

Como comentarios adicionales al código, quiero destacar que la capa con id "ocultando" es la última de la página; el uso de la variable "precargada" inicializada con el número de imágenes a cargar; la programación del evento de la carga de la imagen (nótese el detalle de programarlo antes de la asignación del atributo "src"), y la activación del método "cargada()" al final de la "carga completa".

Evitando redundancias y mejorando la accesibilidad

Cuando creamos un objeto "Image()", reservamos un trozo de memoria que al final es redundante, ya que la nueva imagen será la misma de la etiqueta "img" de la página, así que creo preferible programar el evento de la misma imágen, sin crear un nuevo objeto.

Y sobre la accesibilidad, con el código anterior no se vería nunca la página sin javascript, así que la capa que oculta la página debería estar invisible. Tan solo basta con añadir ese estilo en la declaración inicial.

Para evitar la redundancia, en vez de crear el array y los objetos, usamos directamente el array "document.images", inicializando la variable "precargadas" con su atributo "length".

Como quedaría entonces la función "precargar()" y la inicialización:

<script type="text/javascript" >
<!--
function tag(id)	{return document.getElementById(id);}
var cargadas = function()	{
	tag("ocultando").style.visibility = "hidden";
}
var precargadas;
function precargar()	{
	precargadas = document.images.length;
	for (i = 0, total = precargadas; i < precargadas; i ++)
		if (document.images[i].complete)	precargadas--;
		else	
			document.images[i].onload = function()	{
				if (--precargadas == 0)	cargadas();
			}
	if (precargadas == 0)	cargadas();
}
window.onload = function()	{
	tag("ocultando")	style.visibility = "visible";
	precargar();
}
//-->
</script>

La modificación a destacar en esta versión, es la programación selectiva del evento de carga mediante la consulta del atributo "complete" de los objetos "Image()" (o el tag "img" en este caso). Además podría darse el caso de no necesitarse la precarga, por ejemplo al refrescar la página (el famoso botón F5).

Más variantes

En ocasiones no es necesario implementar una precarga de todas las imágenes, sino algunas de ellas, y para realizar esta precarga "selectiva" bastaría con usar el atributo name o id de las imágenes requeridas, y el código sería una mezcla de las versiones anteriormente. Para los casos de usar el atributo name, debemos procurar que no estén repetidos... o por lo contrario, usar ese mismo nombre en todas las imágenes, pero para no complicar innecesariamente el concepto, vamos a proponer una precarga de imágenes por el id.

<script type="text/javascript" >
<!--
function tag(id)	{return document.getElementById(id);}
var cargadas = function()	{
	alert("imágenes cargadas");
}
var precargadas;
function precargar()	{
	precargadas = precargar.arguments.length;
	for (i = 0, datos = precargar.arguments, total = precargadas; i < total; i ++)
		if (tag(datos[i]).complete)	precargadas--;
		else	
			tag(datos[i]).onload = function()	{
				if (--precargadas == 0)	cargadas();
			}
	if (precargadas == 0)	cargadas();
}
window.onload = function()	{
	precargar("imagen1", "imagen2", "imagen3", "imagen4", "imagen5");
}
//-->
</script>

En este nuevo código hemos modificado también el método "cargadas()" reemplazándolo con una alerta. Otra posible modificación del código consiste en cambiar el reloj del primer ejemplo en la capa "ocultando" por un cuadro con el porcentaje de carga, para eso sería necesario un par de capas anidadas, y en la segunda (interna), cambiar la anchura de la misma con porcentajes en cada decrecimiento de la variable "precargadas".