scottandrew.com

Noteworthy

You're currently viewing a very old version of this website. Follow me to the latest version.

-->

Scripts Para Los Navegadores 6.0

Por Scott Andrew LePera

Traducción al español de Jose Luis Pumarega y Fernando Gutierrez

Revisado en Enero 12, 2001

Por fin alguien entra en razón.

Despues de años de espezanza, rezando, y en algunos casos arrastrándonos, por fín ha salido un navegador que soporta las recomendaciones del World Wide Web Consortium para el Document Object Model.

Cierto, Netscape 6.0 está lejos de ser perfecto. Pero su amplio soporte del DOM-1 y CSS-1 es un gran paso en la dirección correcta. Esperemos que sea el primero en una linea de navegadores populares y exitosos que serán adoptados de modo entusiasta por el público que navega por internet y que nos haga la vida más sencilla a los desarrolladores.

De modo que ¿qué nos importa a nosotros como creadores de scripts? Todo, por que la estructura del DOM expone nuevos y poderosos métodos que nos permiten acceder y manipular los elementos de cualquier documento. Esto va más allá del DHTML y de las etiquetas <DIV>; es el control absoluto sobre cada elemento de la página vía JavaScript.

Con métodos de Script DOM-1, puedes:

- Crear nuevos elementos al vuelo. Olvídate de usar document.write para insertar HTML y CSS en tu página, no más constructores de objetos. El DOM te permite crear elementos completos de la nada.
- Capturar todas las etiquetas del documento, o sólo el texto, sin las etiquetas.
- Insertar nuevo texto, y cambiar o eliminar texto de cualquier elemento sin recurrir a innerHTML o document.write()
- Mover partes completas del documento, o eliminar partes o fragmentos y trabajar con ellos.
- Y lo mejor: Todos estos nuevos métodos deben funcionar en cualquier navegador compatible con DOM-1. Adios a la detección del navegador. No necesitamos construir diferentes versiones de la misma página para distintos navegadores. Un conjunto común de técnicas de script es todo lo necesario.

Este artículo tratará de ser una introducción no excesivamente técnica a los scripts del DOM, prestando relevancia al Netscape 6. Aquellos que quieran seguir estrictamente las referencias (¿?) querrán echar un ojo a la especificación del W3C respecto a DOM y CSS en profundidad, papeles blancos llenos de jerga técnica.

¿Estás dispuesto? Echemos un ojo al asuntillo del DOM.

Parte 1: Nuevas Piezas Del Puzzle

La mejor manera de concebir el DOM es como un árbol gigante, con un tronco que representa el documento, y muchas ramas que representan diferentes elementos de la página. Cada parte del árbol es llamada nodo. Los nodos pueden ser etiquetas, como HTML o XML, o atributos de esas etiquetas, como "align=left" o "size=3". Los nodos también pueden ser cadenas de texto en el documento, como las que estás leyendo ahora mismo.

Un nodo puede ser parte de otro más grande, llamado el padre (parent) de ese nodo. Un nodo puede también ser padre de cualquier número de nodos hijos (child nodes). Por ejemplo, el documento es un nodo. Si el documento contiene imágenes, el documento es el padre de los nodos imagen, que son sus hijos. Al mismo tiempo las imágenes son padres de sus atributos, como nodos hijos. Exactamente igual que un árbol: es padre de la rama, que es padre de la hoja. Así es el Document Object Model.

Buf!! ¿lo has cogido?

En los navegadores de cuarta generación, estábamos limitados por el número de nodos que podíamos manipular con JavaScript. Con soporte DOM completo aplicaciones como Netscape 6 y Mozilla podremos acceder a cualquier elemento -o nodo-. El cómo somos capaces de acceder a ellos depende principalmente del atributo ID del elemento. Echemos un ojo a dos importantes métodos DOM que nos permiten hacer todo esto.

getElementById()

Si conoces el ID de un elemento, puedes acceder a el con el nuevo método getElementById(). Simplemente pásale el ID del elemento como parámetro y devolverá la referencia a ese elemento.

<div id="miElemento"></div>

Para recoger esto en JavaScript, deberíamos usar:

x = document.getElementById("miElemento");

X es ahora una referencia al elemento <DIV>. A partir de esto, podemos usar x para manipular la etiqueta <DIV>, como veremos en breve.

getElementsByTagName()

Otro modo de recoger elementos es usando getElementByTagName(). Este método toma el tipo de etiqueta (tag) como parámetro y devuelve una colección (o array) de todos los elementos con ese tipo en el documento.

Por ejemplo, si quisieras recoger todas las imágenes del documento, podrías hacer esto:

x = document.getElementsByTagName("IMG");

X es ahora una colección de referencias a los elementos <IMG> del documento. Para acceder a un elemento particular en la colección, digamos la primera imagen, podríamos referirnos a ella por su número ordinal o posición dentro de la colección.

x = document.getElementsByTagName("IMG");
firstImage = x.item(0) ;

getElementsByTagName() recoge todos los elementos coincidentes tanto si tienen un atributo ID como si no, lo cual es extremadamente útil para etiquetas a las que normalmente no se asignan ID, como <P> y <TD>.

getElementById() y getElementsByTagName() son modos muy potentes de atravesar el árbol DOM hacia las ramas más lejanas. En vez de usar document.algo.algoMás para alcanzar un elemento, puedes dirigirte directamente a él, o a elementos de su mismo tipo, con estos dos métodos.

Parte 2: Magia Elemental

getElementById() y getElementsByTagName() se utilizan para recuperar elementos. Pero los elementos tienen su propio conjunto de métodos que facilitan la manipulación de elementos sin necesidad de realizar scripts.

Considera la siguiente etiqueta de imagen para nuestros próximos ejemplos:

<image id="myImg" src="jabberwocky.gif" align="left"></img>

Para recuperar este elemento en Java-Script, usaríamos:

x = document.getElementById("myImg")

Ahora observa lo que los métodos DOM x tienen disponible para ello:

getAttribute()

getAttribute() devuelve el valor del atributo que tú le pases. Para obtener el valor SRC de esta etiqueta via Java-Script, podríamos hacer esto:

myValue = x.getAttribute("src")

myValue podría entonces contener la cadena "jabberwocky.gif."

setAttribute()

setAttribute() te permite asignar el valor del atributo que tú le pases. Utiliza dos argumentos: el nombre del atributo y el valor que se le asigna.

Para cambiar el valor ALIGN de esta etiqueta de izquierda por el de derecha, podemos hacer esto:

x.setAttribute("align","right")

removeAttribute()

removeAttribute() simplemente quita el atributo que le pases. Para quitar el atributo ALIGN de esta etiqueta:

x.removeAttribute("align")

getElementsByTagName()

Hey, espera. ¿No hemos visto esto antes? Lo has pillado. Lo vimos originalmente en el objeto documento, pero cada elemento también dispone de este método. La diferencia es que el método del documento devuelve todas las etiquetas que coincidan para todo el documento, mientras que el método del elemento devuelve las etiquetas coincidentes sólo para ese nodo.

Veamos un ejemplo:

<table id="tableOne">
  <tr>
    <td>Esta es la celda Uno</td>
    <td>Esta es la celda Dos</td>
    <td>Esta es la celda Tres</td>
  </tr>
</table>

<table id="tableTwo">
  <tr>
    <td>Esta es la celda Cuatro</td>
    <td>Esta es la celda Cinco</td>
    <td>Esta es la celda Seis</td>
  </tr>
</table>

Tenemos dos tablas, tableOne y tableTwo respectivamente. Podemos usar getElementsByTagName() con el documento para recuperar todos los elementos de celda de tabla en esta página, de esta forma:

x = document.getElementsByTagName("TD")

Lo cuál nos devolverá x como una colección de las seis etiquetas <TD>. Pero quizás preferiríamos extraer sólo las etiquetas de tableTwo. Para hacer esto, podemos usar primero getElementById() para recuperar el elemento tabla, y entonces usar getElementsByTagName() para recuperar sólo aquellas celdas de este elemento tabla:

x = document.getElementById("tableTwo")
tableCells = x.getElementsByTagName("TD")

tableCells se convierte ahora en una colección de sólo tres etiquetas <TD> contenidas en tableTwo.

Parte 3: Gran Papá document

Echemos un ojo hacia atrás al Gran Nodo Padre de Todas Las Cosas - el documento. Ya sabemos que el documento tiene métodos para recoger cualquier elemento a placer. Pero, ¿qué mas puede hacer? Muchas, muchísimas cosas.

El documento puede crear nuevos nodos al vuelo, destruirlos, moverlos alrededor de la página, lo que sea. Es perversamente divertido. Y lo hace sin usar document.write() o constructores de nuevos objetos (No te asustes, esos métodos siguen ahí). Echemos un ojo a los métodos de Script de DOM que permiten que esto ocurra.

createElement()

createElement() permite crear nuevas etiquetas. ¡Si! ¡Puedes crear estructuras HTML completas de la nada. ¡Sorprende e impresiona a tus amigos! ¡Vamos a hacer uno ahora mismo!

miImagen = document.createElement("IMG");

¡Ta-da! ¡Simple como un cubo! ¡Acabamos de crear una nueva etiqueta <IMG>! Podemos hacer esto para crear qualquier etiqueta válida que queramos.

Pero espera. ¿donde se ha creado la imagen? Aún no está en el documento. ¿Por qué? Por que para cada nuevo elemento que quieras que sea mostrado, debes decirle al documento dónde añadirlo dentro del árbol del mismo documento.

Esto es algo importante que debes de entender cuando uses creaeteElement(). No estás insertando HTML como podrías hacerlo con document.write(). A cambio, estás creando un nuevo elemento crudo, esperando a ser definido e insertado como un hijo del nodo document. Está flotando en el Limbo, esperando a que alguien le de forma y figura. En el ejemplo anterior, nuestra etiqueta recien nacida <IMG> no tenía ni ID ni SRC ni un lugar en el documento aún. Así que definámoslo:

miImagen.setAttribute("id","imagenUno"); miImagen.setAttribute("src","jabberwocky.gif");

Ahora todo lo que necesitamos es añadirlo al cuerpo del documento. Para ello, usaremos el método para nodos DOM appendChild():

docBody = document.getElementsByTagName("body").item(0); docBody.appendChild(miImagen);

Fíjate ahora cómo hemos recogido la etiqueta <BODY> del documento y la hemos usado en vez de añadirlo directamente al documento. El motivo de esto es que <BODY> es el nodo dentro del documento en el que todo se muestra.

appendChild() es un método que funciona en cualquier nodo. En el próximo ejemplo, construiremos una tabla de 3x3 de dentro a fuera, primero crearemos el cuerpo, filas y columnas, y las añadiremos a la tabla principal. Finalmente añadiremos la tabla completa al cuerpo del documento.

docBody = document.getElementsByTagName("body").item(0);
myTable = document.createElement("TABLE");
myTable.id = "TableOne";
myTableBody = document.createElement("TBODY");
for (i = 0; i < 3; i++){
  row = document.createElement("TR");
  for (j = 0; j < 3; j++){
    cell = document.createElement("TD");
    row.appendChild(cell);
  }
  myTableBody.appendChild(row);
}
myTable.appendChild(myTableBody);
docBody.appendChild(myTable);

Puedes ver por qué tiene sentido construir códigos HTML complejos comenzando primero por los nodos y luego ensamblarlas en estructuras mayores.

createTextNode()

Hmm. Nuestra tabla está un poco, bien, vacía. Añadamos algo de texto con el método DOM createTextNode(). createTextNode() simplemente crea un nodo genérico que no es otra cosa que una cadena de texto. Puedes añadirlo a otros nodos para darlos contenidos dinámicos. Añadamos algo de código a nuestro bucle crea-celdas.

for (j = 0; j < 3; j++){
  cell = document.createElement("TD");
  textVal = "Cell" + i + "_" + j;
  textNode = document.createTextNode(textVal);
  cell.appendChild(textNode);
  row.appendChild(cell);
}

Ahora nuestras celdas tienen algo de texto. ¿Y qué pasa con atributos como BGCOLOR o VALIGN? Podemos asignarlos mediante el método DOM setAttribute():

for (j = 0; j < 3; j++){
  cell = document.createElement("TD");
  cell.setAttribute("WIDTH","50");
  cell.setAttribute("HEIGHT","50");
  textVal = "Cell" + i + "_" + j;
  textNode = document.createTextNode(textVal);
  cell.appendChild(textNode);
  row.appendChild(cell);
}

Vamos a acabar. Aquí está el código completo para nuestra funión generadora de tablas.

function construyeTabla(){
  docBody = document.getElementsByTagName("body").item(0);
  myTable = document.createElement("TABLE");
  myTable.id ="TableOne";
  myTable.border = 1;
  myTableBody = document.createElement("TBODY");
  for (i = 0; i < 3; i++){
    row = document.createElement("TR");
    for (j = 0; j < 3; j++){
      cell = document.createElement("TD");
      cell.setAttribute("WIDTH","50");
      cell.setAttribute("HEIGHT","50");
      textVal = "Cell" + i + "_" + j;
      textNode = document.createTextNode(textVal);
      cell.appendChild(textNode);
      row.appendChild(cell);
    }
    myTableBody.appendChild(row);
  }
  myTable.appendChild(myTableBody);
  docBody.appendChild(myTable);
}
window.onload = construyeTabla;

Llamamos a la funcion construyeTabla() en el evento window.onload dado que tenemos que estar seguros de que el elemento <BODY> existe antes de añadirle una tabla. Prueba a copiar el código y pegarlo entre etiquetas <SCRIPT> en el head de un documento HTML en blanco, y abrelo con IE 5.0 o Netscape 6.0 para ver el efecto completo.

Parte 4: Siguiendo A Tus Nodos

¿Hemos llegado hasta aquí? Bien, aún queda mucho más. El DOM provee de un conjunto de métodos y propiedades que todos los nodos pueden usar. Muchos de ellos tienen que ver con manipulación de nodos hijos o padres. Echemos un ojo:

(ACTUALIZADO 11.17.00: La versión final de Netscape 6 contiene un bug que puede colgar al navegador cuando se usen los métodos removeChild o replaceChild, así que proceded con precaución. Ojo al parche -nunca mejor dicho- de la próxima actualización.)

Métodos para Nodos

appendChild(hijo) Ya lo has visto en acción, de hecho. Permite añadir un nuevo nodo a otro existente.

replaceChild(nuevo,viejo) Permite eliminar un nodo y reemplazarlo por otro. Nuevo es el ID del nuevo nodo, y viejo es el ID del nodo que quieres reemplazar.

nuevaFila = document.createElement("TR");
cuerpoTabla = document.getElementById("miTabla");
viejaFila = document.getElementByTagName("TR").item(0);
cuerpoTabla.replaceChild(nuevaFila,viejaFila);

removeChild(hijo) eliminará el nodo hijo del documento. Hijo es el nombre del nodo que quieres eliminar. El siguiente ejemplo elimina una fila de un elemento Table.

cuerpoTabla = document.getElementById("miTabla");
hijoUno = document.getElementByTagName("TR").item(0);
tableBody.removeChild(hijoUno)

Piensa que al eliminar un elemento eliminas además sus hijos.

insertBefore(nuevo,referencia) te permite insertar un nuevo nodo justo antes que otro. Nuevo es el nuevo nodo que quieres insertar. Referencia es el nombre del nodo antes del cual quieres insertarlo.

nuevaFila = document.createElement("TR");
cuerpoTabla = document.getElementById("miTabla");
todasLasFilas = tableBody.getElementByTagName("TR");
ultimaFila = allRows.item(allRows.length-1);
tableBody.insertBefore(nuevaFila,ultimaFila);

hasChildNodes() devuelve un valor Booleano true si el nodo tiene hijos.

Fíjate en que appendChild(), replaceChild(), removeChild() e insertBefore() sólo aceptan referencias a elementos, no los ID de esos elementos, como argumentos. Por ejemplo, si quisieramos eliminar una etiqueta con el ID "miEtiqueta" la siguiente sintaxis sería incorrecta:

removeChild("miEtiqueta");

La correcta sería:

removeChild(document.getElementById("miEtiqueta"));

Propiedades de los Nodos

En adición a esos métodos, los nodos tienen las siguientes propiedades:

nodeName devuelve simplemente el nombre de la etiqueta HTML como una cadena de caracteres. No es lo mismo que el atributo NAME de la etiqueta, si no una cadena que representa el tipo de etiqueta. Por ejemplo, el nodeName de una celda de una tabla devolverá "TD". Esto es útil para saber si una etiqueta es de un tipo particular:

x = document.getElementById("miElemento");
if (x.nodeName == "IMG") alert("miElemento es una etiqueta IMG");

nodeType devuelve un entero que representa el tipo de nodo de un elemento (NO el tipo de etiqeta) Las que con toda certeza usarás con más frecuencia serán elementos (1), atributo (2) y texto (3). Esto es útil para saber si un nodo es de un tipo determinado.

x = document.getElementById("miElemento");
if (x.nodeType == 1) {alert("miElemento es un nodo de tipo elemento");}
else if (x.nodeType == 2) {alert("miElemento es un nodo de tipo atributo");}
else if (x.nodeType == 3) {alert("miElemento es un nodo de tipo texto");}

nodeValue refleja el valor de un nodo dado. Esto se usa comunmente para obtener y cambiar el contenido o valor de un nodo. Para recoger el valor del quinto nodo en nuestro elemento:

a = document.getElementById("miElemento");
b = a.childNodes.item(4).nodeValue;

Para los nodos de atributo, nodeValue devuelve el valor del atributo. Para nodos de tipo texto, nodeValue devuelve el texto como una cadena.

parentNode es una referencia al nodo padre del nodo en cuestión. Es útil cuando no estás seguro de donde estás o de cual es el nodo padre de otro nodo:

x = document.getElementById("miElemento");
xParent = x.parentNode;

childNodes es una colección de los nodos hijos de un nodo en particular. Puedes usar esto para saber el número de hijos que tiene un nodo, o acceder a caulquiera de sus hijos mediante el índice de esa colección.

Por ejemplo, para conocer el número de hijos que tiene un nodo, podrías hacer esto:

a = document.getElementById("miElemento");
b = a.childNodes.length;

Para acceder al tercer nodo hijo del elemento:

a = document.getElementById("miElemento");
b = a.childNodes.item(2);

firstChild y lastChild apuntan directamente al primer y último nodo de la colección childNodes. Es, sencilamente, una manera de acceder a ellos.

a = document.getElementById("miElemento");
PrimerNodoDea = a.firstChild;
UltimoNodoDea = a.lastChild;

Parte 5: Sumario

Uaaaaaaj. Hay un montón de material para recordar. Las siguientes tablas recogen los distintos métodos DOM y propiedades que hemos cubierto.

Métodos del Documento:

getElementById(id) Devuelve una referencia al elemento llamado.
getElementsByTagName(nombre) Devuelve una colección de todos los elementos coincidentes en el documento.
createElement(nombre) Crea un nuevo elemento del tipo nombrado.
createTextNode(texto) Crea un nuevo nodo de puro texto.

Métodos de los Elementos:

getAttribute(id) Devuelve el valor del atributo pasado como parámetro.
setAttribute(id,valor) Asigna un nuevo valor al atributo.
removeAttribute(id) Elimina el atributo pasado como parámetro junto con su valor.
getElementsByTagName(nombre) Devuelve una colección de todos los elementos coincidentes en el nodo.

Métodos de los Nodos:

appendChild(hijo) Añade un nuevo nodo hijo al nodo.
removeChild(hijo) Elimina un nodo hijo del nodo .
replaceChild(nuevoHijo,viejoHijo) Reemplaza un nodo hijo antiguo por otro nuevo.
insertBefore(nuevoHijo,referencia) Inserta un nodo antes del anterior en la jerarquía.
hasChildNodes() Devuelve un true booleano si el nodo tiene nodos hijo.

Propiedades de los Nodos:

nodeName Contiene el nombre del nodo como una cadena de texto.
nodeType Contiene el tipo de nodo como un entero.
nodeValue Contiene el valor de un nodo en formato útil.
parentNode La referencia del nodo padre de este nodo.
childNodes Una colección que contiene referencias a los nodos hijos de este nodo en cuestion.
firstChild La referencia al primer nodo hijo en la colección childNodes.
lastChild La referancia al último nodo hijo en la coleción childNodes.

Conclusión

Como probablemente estés viendo, la funcionalidad del W3C DOM hace posible tener documentos extremadamente dinámicos. Cuando el resto de los navegadores se adecúen al standard, nuestras vidas como desarrolladores serán mucho más sencillas. Podremos abandonar nuestros scripts de detección de navegadores, y ser libres de programar codigo auténticamente multi navegador sin dolores de cabeza. Es posible.

Actualmente, depende de ti (¿qué significa esto, en cualquier caso?) seguir aprendiendo las especificaciones DOM-1 y CSS-1. Hemos cubierto la mayor parte del material importante aquí, pero hay mucho más de lo que ofrece el DOM. Emplea algo más de tiempo en el sitio del W3C, familiarízate con las especificaciones y estarás listo para hacerlas pasar por el aro.

Feliz scripting!

Aprende Más

- Comenta este tutorial.
- Encuentra algo más sobre Netscape 6 en developer.netscape.com
- Aprende acerca del DOM, CSS y soporte standard en http://www.w3c.org/
- Unete a la causa del Web Standards Project http://www.webstandards.org/