Què fa la connexió mentre es llegeix la pàgina? Sovint, res. En canvi quan, després, l'usuari fa clic sobre un enllaç, ha d'esperar que l'arxiu arribi i, si és una imatge gran, pot trigar. Amb els temporitzadors de JavaScript i una mica d'estratègia podem aprofitar el "temps mort" en què la connexió està aturada per carregar el que s'ha de veure a continuació.
Carregar imatges en segon pla
Això no sempre està ben vist, i s'ha d'utilitzar amb molta cura. Hem d'estar segurs (o gairebé) que l'usuari voldrà veure allò que li colem al seu disc dur sense permís, i que s'estimarà més que fem aquest joc brut que no pas haver d'esperar.

En aquesta pràctica només veurem un exemple: una col·lecció de fotos sobre pintures de Vincent Van Gogh. Quan s'acabi de mostrar la primera imatge, engegarem un procés en segon pla que carregarà les altres, de manera que, quan l'usuari les vulgui veure, les pugui tenir a l'instant.

Tot i que els aspectes gràfics es tracten al mòdul següent, aquí iniciarem el treball amb imatges des de JavaScript. Ens servirà d'aperitiu.

  Conceptes JavaScript en aquest capítol
Event onLoad() : (aplicat a imatges) executa una acció quan la imatge està carregada.
Objecte document.images : Array amb totes les imatges d'una pàgina
Propietat complete : determina si una imatge ja està totalment carregada.
Mètode new Image() : crea un objecte imatge de JavaScript, buit.
Propietat src : adreça de l'arxiu que conté una imatge

  1.- Variables globals i estructura
Les accions en segon pla són un concepte genèric que es pot aplicar en molts llocs i de diverses maneres. Aquí plantegem una aplicació d'objectiu didàctic, per veure com es fa, i això condiciona l'estructura. Només és un exemple, l'important és observar l'estratègia.

Utilitzarem JavaScript en dos llocs: a la capçalera <head> hi posarem les variables globals i les funcions, i al cos <body>, el bucle que farà els enllaços amb les fotos. L'aplicació suposa que totes les fotos són a la mateixa carpeta, que tenen format .jpg, i que el seu nom és la paraula "vg0", seguida d'un número entre 1 i 9: vg01.jpg, vg02.jpg, ... vg09.jpg.

Comencem amb les variables globals:
  - model : la primera part del nom de les fotos: images/vg0. S'assigna des del cos.
  - nImg : el número de la imatge. S'incrementa quan la imatge anterior està carregada.
  - nMax : número màxim de fotos. S'utilitza per saber quan s'ha d'acabar.
  - tm : referència al temporitzador.
  - foto : Array d'objectes imatge. És el que anirem omplint amb les fotos.

També a la capçalera hi escriurem tres funcions, que veurem en detall més endavant:
  - fesImg() : resol el nom de cada foto, l'afegeix a l'Array i n'informa.
  - tempsMort() : fa comprovacions i gestiona el temps mort.
  - segonPla() : rep els paràmetres, assigna les variables globals i engega el procés.

Passem al cos. Posem un formulari amb un camp de text que ens informarà sobre el número d'imatge que s'està carregant. A continuació, situem una imatge, amb la particularitat que assignem l'event onLoad="segonPla('images/vg0','9')". És a dir, quan la foto s'hagi acabat de carregar (onLoad), s'executarà la funció "segonPla" amb dos paràmetres: el model de foto i el número màxim.

Després, obrim JavaScript i fem el bucle dels enllaços. Tot i que no és la forma habitual, aquí hem fet el famós enllaç buit i assignem l'event onClick, de manera que es substitueixi la foto que es mostra en pantalla: onClick="img1.src=\'' + imatge + '\';return false". Altre cop utilitzem l'antibarra (\) per indicar que el que ve a continuació són unes cometes simples o apòstrof.

<script language="javascript">
var model, nImg, nMax, tm
var foto = new Array()

function fesImg() ...

function tempsMort() ...

function segonPla() ...

</script>

<body>
<center>
<form name="form1">
<b>Carregar imatges en segon pla:</b> &nbsp;
<input type="text" name="actual" size="1" value="0" >
</form>
<img name="img1" src="images/vg01.jpg" border="1" width="360"
height="260" onLoad="
segonPla('images/vg0','9')"><br><br>&nbsp;
<script language="javascript">
for( i=1; i<10; i++ ) {
  var imatge = 'images/vg0' + i + '.jpg'
  document.writeln( '[<a href="" onClick="img1.src=\'' + imatge + '\';return false">' )
  document.writeln( '<b> ' + i + ' </b></a>] &nbsp;' )
}
</script>
...

 
2.- Funció segonPla( m, n )
Suposem que s'acaba de carregar la primera imatge. Tal com hem previst, cridarà la funció "segonPla" amb dos paràmetres, el model i el número màxim de fotos. La funció els rep i els assigna a dues variables locals, m i n. Primer, "neteja" el temporitzador.

A continuació cal comprovar que totes les imatges de la pàgina s'han acabat de carregar. En aquest cas, no caldria, perquè sabem que només n'hi ha una, però és un bon hàbit. Utilitzem una variable ok = true i fem un bucle que repassi les imatges del document. Si alguna encara no s'ha completat, assignem ok = false i sortim del bucle (break).

L'expressió "! document.images[ i ].complete" utilitza l'operador de negació (!) i consulta la propietat complete de la imatge actual.

Si "ok" és encara true és que totes les imatges de la pàgina s'han carregat. Aleshores, assignem les variables globals i fem un temporitzador a la funció "tempsMort()". En cas contrari, tornem a intentar-ho al cap d'un temps utilitzant recursivitat, és a dir, fem un temporitzador a la pròpia funció "segonPla" amb els paràmetres que havia rebut.

function segonPla( m, n ) {
  clearTimeout( tm )
  var ok = true
  for( i=0; i<document.images.lenght; i++ ) {
    if( !document.images[i].complete ) {
      ok = false
      break
    }
  }
  if( ok ) {
    model = m
    nMax = parseInt( n )
    nImg = 0
    tm = setTimeout( 'tempsMort()', 100 )
  }
  else tm = setTimeout( 'segonPla("' +m + '","' + n + '")', 100 )
}

 
3.- Funció tempsMort()
Bé, si arribem a aquesta funció és que som en un temps mort, la pàgina ha acabat la seva càrrega d'imatges. Com que hi haurem arribat amb un temporitzador, primer, el netegem, i, seguidament, comprovem si també s'han carregat les fotos en segon pla. Si és així, tornem.

La línia: if( nImg>=nMax || nImg==nImg-1 ) return
Vol dir: si el número d'imatge ha arribat al màxim o és el mateix de la vegada anterior, torna.

A continuació hem de fer una distinció: si la imatge encara no és la segona, incrementem el comptador i cridem la funció "fesImg()". Si no, abans hem de comprovar que la imatge anterior ja és completa. Si no ho féssim, el navegador intentaria carregar-les totes alhora. En qualsevol cas, la funció, recursiva, acaba cridant-se a ella mateixa amb un temporitzador.

function tempsMort() {
  clearTimeout( tm )
  if( nImg>=nMax || nImg==nImg-1 ) return
  if( nImg<2 ) {
    nImg++
    fesImg()
  }
  else {
    if( foto[nImg-1].complete ) {
      nImg++
      fesImg()
    }
  }
  tm = setTimeout( 'tempsMort()', 100 )
}
 
4.-
Funció fesImg()
Aquí s'hi arriba en fila. Quan aquesta funció processa la primera foto en segon pla és perquè totes les imatges de la pàgina són completes i, quan processa cadascuna de les altres, és perquè està totalment carregada l'anterior.

És ben simple: crea el nom de la imatge a partir del model, el número i ".jpg". A continuació, fa un nou objecte "Image()" i assigna el nom que ha creat a la propietat .src. Amb això el navegador en té prou per començar la càrrega de la foto.

Finalment, com que l'objectiu de l'exemple és didàctic i volem saber què fa la funció, escriu el número d'imatge al camp del formulari que havíem posat per aquest motiu.

function fesImg() {
  var nomImg = model + nImg + '.jpg'
  foto[nImg] = new Image()
  foto[nImg].src = nomImg
  document.forms[ 0 ].actual.value = nImg
}
 
5.-
Tot plegat
Per veure el codi de l'aplicació, l'obrim amb qualsevol dels botons anteriors i utilitzem el visor de codi font del nostre navegador.

Resumint, tenim una pàgina que carrega una foto i, quan ho ha fet, engega una funció que comprova si queden feines pendents. Quan veu que no en queden, engega una altra funció que carrega una foto en segon pla i, un cop acabada, una altra, fins al màxim que li hem dit.

La principal dificultat de treballar amb temporitzadors és psicològica: les diverses funcions no s'executen en l'ordre en què les hem escrites, sinó quan es "dispara" el temporitzador. Això fa que la lectura del codi resulti difícil de seguir, perquè hem d'imaginar que "s'ha esgotat el termini i aleshores salta a ...". Potser haurem de revisar el codi diverses vegades.

A l'exemple hem posat a tots els temporitzadors un temps d'espera de 100 mil·lisegons. És així perquè escrivim el número en un quadre de text i volem que es vegi. Quan s'aplica a una pàgina "de veritat", normalment pretenem tot el contrari: que no es perdi temps i que no es noti que fem feina en segon pla. Fàcil, traiem el quadre de text i posem 1 mil·lisegon.

Algú pot opinar que no cal tant codi, i que amb un bucle n'hi hauria prou. Això està molt bé per a la precàrrega (ho veurem al mòdul següent) però aquí no. Un bucle deixaria al navegador la feina de gestionar la càrrega, i aquest ho posaria a la seva "cua" de processament. Quan l'usuari fes clic en un vincle, hauria d'esperar el seu torn mentre es carreguen les imatges.

Una última observació: tot això s'engega des de l'event "onLoad" en acabar la càrrega de cada foto. D'aquesta manera, si l'usuari fa clic en un vincle quan encara no s'han carregat totes, es mostra la que demana i es torna a començar el procés de comprovació. Si l'algoritme és correcte, haurem aprofitat el temps mort al màxim. Time is on my side.