Si utilitzem només HTML, o fem les pàgines amb un editor (Dreamweaver), els marcs tenen més inconvenients que no pas avantatges. Són fàcils de crear, però el seu comportament és diferent segons el navegador. A més, és difícil mantenir-los sincronitzats i, és clar, al cap d'una estona de fer clic en vincles i tornar enrera, ja no sabem on som.

Aleshores, quan hem d'usar marcs? Doncs, quan els avantatges superin els inconvenients, o quan no hi hagi altre remei. Això sí, abans de començar hem de tenir clar que haurem de dedicar molta atenció a la sincronització.
Mrcs interactuant
Algunes de les eines que hem dissenyat per aquest curs es basen en una estructura de marcs. En tots els casos resultaven imprescindibles o, com a mínim, eren l'opció més efectiva i amb menys complicacions.

A l'Inspector d'Objectes, per exemple, el marc superior conté els controls, el de la dreta carrega la pàgina i el de l'esquerra l'analitza. La sincronització falla quan l'usuari segueix un vincle de la pàgina que s'analitza. El marc de l'inspector s'actualitza, però l'adreça de la pàgina - al quadre del marc superior, un control "file" - no canvia.

FerÍndex, en canvi, necessita un marc ocult per poder obrir les pàgines i extreure'n el títol. Ull viu amb aquesta tècnica: com que JavaScript només pot analitzar pàgines carregades en un navegador, amb aquest truc aconseguim veure-les i que no es noti.

  Conceptes JavaScript en aquest capítol
Objecte window.frames : Array amb tots els marcs d'una finestra.
Propietat frames.length : número de marcs que conté una finestra.
Propietat parent : referència a la finestra que defineix una estructura de marcs.

  1.- Crear l'estructura de marcs
Si es crea l'estructura de marcs en HTML és probable que les mides no siguin correctes en algun dels dos navegadors. Per tal d'evitar-ho, podem usar variables i codi JavaScript que ens facin les mides exactes per a cada navegador. Aquí proposem observar l'estructura que hem utilitzat a l'inspector d'objectes.

Un truc basat en l'estructura és el del frame zero: un marc ocult (es veu una ratlleta, si ens hi fixem bé) que s'utilitza com a finestra "visible per a la pàgina, invisible per a l'usuari". Es tracta de distribuir l'espai d'una finestra de manera que un dels marcs tingui dimensions 0. Ho hem d'utilitzar a l'eina FerIndex per tal de poder obrir les pàgines que analitza. No és l'única estratègia, però és pràctica, ràpida i neta, perquè gairebé no es nota.

Sempre que fem una estructura de marcs cal recordar que les versions 4 de Netscape no els actualitzen quan es redimensiona la finestra, o bé ho fan malament. Amb Dreamweaver es pot solucionar. Per això, anem al menú "Comandos" i seleccionem "Agregar/Quitar reparación de cambio de tamaño de Netscape ...". Podem aprofitar l'espai per a scripts que genera per inserir-hi les instruccions de correcció de mesures.

Al codi podem veure un bocí del que ha escrit Dreamweaver en lletra gris.

<script language="javascript">
<!--

function MM_reloadPage(init) {
  ...
}
MM_reloadPage(true);

var altura = (document.layers) ? 32 : 27;
document.writeln('<frameset rows="'+altura+',*" frameborder="0" framespacing="0">');
// -->
</script>
  <frame name="menu" src="menu.htm" scrolling="no">
  <frameset cols="280,*" frameborder="1" framespacing="1">
    <frame name="inspect" src="ajut.htm" scrolling="auto">
    <frame name="pag" src="portada.htm" scrolling="auto">
  </frameset>
</frameset>
<noframes>
  <body>
  <p>Aquesta pàgina utilitza marcs, i el vostre navegador no els pot mostrar.</p>
  </body>
</noframes>
 
2.- Referenciar marcs i objectes
En una estructura de marcs hi ha un document principal i diversos fills. Cada marc és una instància de l'objecte window, amb totes les seves propietats i mètodes. Normalment, l'arxiu on es defineix l'estructura no conté instruccions operatives, només conté les de creació dels marcs i, en tot cas, un bocí de codi sota l'etiqueta <noframes>.

Per tal de manipular els marcs, el DOM proporciona l'objecte window.frames, un Array on cada element és un objecte del tipus "window" que conté la pàgina d'un marc. Com tots els Arrays, podem referenciar els elements a partir del seu índex.

L'única propietat de l'objecte frames que no té window és length, que ens informa sobre el número de marcs continguts en una finestra. Amb aquest número, per exemple, podem fer un bucle que el vagi recorrent i ens informi sobre les característiques de cada marc.

Els marcs tenen un nom que s'assigna en el moment de la seva creació. Des de la finestra principal ens podem referir a un marc pel seu nom. Des de cada marc, ens podem referir a la finestra principal utilitzant la propietat parent. Si, des d'un marc, ens volem referir a objectes d'un altre, ho hem de fer a partir de "parent" i hem d'usar l'índex o el nom del marc:

  parent.marc1.document.images[0].src = 'imatge1.gif'  ... o bé ...
  parent.frames[0].dcocument.forms[0].nom.value = 'unNomQualsevol"

A continuació tenim un exemple que mostra diverses possibilitats. Crearem una estructura amb dos marcs, al 50%. Els anomenem "f1" i "f2". Els arxius que es carreguen també tenen aquests noms per simplificar-ho, però poden tenir-ne qualsevol altre. Un cop hem identificat els marcs pel seu nom, el dels arxius no té cap importància.

<html>
<title>Exemple</title>
<frameset cols="50%,50%">
  <frame name="f1" src="f1.htm">
  <frame name="f2" src="f2.htm">
</frameset>
</html>


Al marc de l'esquerra, arxiu f1.htm, farem un formulari amb tres botons, i els definirem l'event onClick. El primer botó carregarà la funció posaNom, que és en aquest mateix arxiu. El segon executarà la funció "hola()" en el marc f2, que encara no hem fet. El tercer tornarà a carregar una funció del seu propi arxiu, "obrePag()".

La funció posaNom() rep un paràmetre, txt, i l'assigna a un camp de text en un formulari de l'altre marc, que encara no hem fet. Per referenciar l'objecte utilitza la sintaxi d'índex:

  parent.frames[1].document.forms[0].nom.value = txt

Hem dit que l'últim botó executa una funció, obrePag(). Rep com a paràmetre una adreça, url, i la utilitza per actualitzar el marc de la dreta. Utilitza la sintaxi del nom:

  parent.f2.location.replace( url )

Després, prepara un temporitzador que tornarà a carregar, també a la dreta, l'arxiu original, f2.htm. Com podem veure, tots tres botons acaben actuant sobre el marc de la dreta, a través de funcions pròpies (botons 1 i 3) o, directament, "cridant" una funció de l'altre marc (botó 2). Això només és possible, evidentment, si sabem amb seguretat quina pàgina contindrà.

<html>
<head>
<script language="javascript">
function posaNom(txt){
  parent.frames[1].document.forms[0].nom.value = txt
}
function obrePag(url){
  parent.f2.location.replace( url )
  tm = setTimeout( 'parent.f2.location.replace("f2.htm")', 2000 )
}
</script>
</head>
<body bgcolor="#CCDDFF">
<form name="Form1">
<h3>Actua sobre la dreta</h3>
<input type="button" value="Nom (camp)" onClick="
posaNom('Un Nom')"><br>
<input type="button" value="Hola (funció)" onClick="
parent.f2.hola('esquerra')"><br>
<input type="button" value="Obrir pàgina" onClick="
obrePag('prova.htm')">
</html>


Bé, només ens queda fer l'arxiu f2.htm, que es mostrarà al marc de la dreta. Aquest ha de tenir, de moment, un camp i una funció condicionats per l'arxiu anterior.

Al cos hi posarem un altre formulari amb tres controls. El primer serà el camp "nom" que, com hem vist, s'ha de poder actualitzar des de l'esquerra. El segon serà un botó que engegui la mateixa funció "hola()" que s'engegava des de l'altre marc. Ara, però, és al mateix arxiu i, per tant, s'ha de cridar d'una altra manera ... o no? Doncs, com vulguem, si ho escrivim com a l'altre, parent.f2.hola(), també funcionarà. La conclusió és evident.

L'últim control del formulari serà un altre botó, que fa una feina, si més no, curiosa: executa una funció de l'altre marc. El que té d'especial és que la funció actua sobre el marc de la dreta, que és el mateix que conté el botó. De manera que un control de la dreta li demana a una funció de l'esquerra que actualitzi un altre control de la dreta. Com ho veiem?

Finalment, la funció "hola()", que s'utilitza des de tots dos marcs, només fa una alerta a partir del text que rep com a paràmetre. A hores d'ara ja queda clar que els controls de formulari i les funcions són accessibles des de tots els marcs. També ho són les variables i tots els altres objectes, si utilitzem la referència correcta.

<html>
<head>
<script language="javascript">
function hola(txt){
  alert( 'Hola!\nEt saluda el marc: ' + txt )
}
</script>
</head>
<body bgcolor="#FFEEBB">
<form name="Form2">
<h3>Reacciona i actua</h3>
Nom: <input type="text" name="nom"><br>
<input type="button" value="Hola (funció)" onClick="
hola('dreta')"><br>
<input type="button" value="Nom (camp)" onClick="
parent.f1.posaNom('Un Altre')">
</html>