El Modelo de Objetos del Documento (DOM) permite ver el mismo documento de otra manera, describiendo el contenido del documento como un conjunto de objetos que un programa Javascript puede actuar sobre ellos.

¿Qué es el Modelo de Objetos del Documento?

Acorde al W3C el Modelo de Objetos del Documento es una interfaz de programación de aplicaciones (API) para documentos validos HTML y bien construidos XML. Define la estructura lógica de los documentos y el modo en que se accede y manipula.

El DOM permite un acceso a la estructura de una página HTML mediante el mapeo de los elementos de esta página en un árbol de nodos. Cada elemento se convierte en un nodo y cada porción de texto en un nodo de texto. Para comprender más fácilmente véase el siguiente ejemplo:

<body>
<p>Esto es un párrafo que contiene <a href="#">un enlace</a> en el medio. </p>
<ul>
<li>Primer punto en la lista</li>
<li>Otro punto en la lista</li>
</ul>
</body>

Como puede verse un elemento [a] se encuentra localizado dentro de un elemento [p] del HTML, convirtiéndose en un nodo hijo, o simplemente hijo del nodo [p], de manera similar [p] es el nodo padre. Los dos nodos li son hijos del mismo padre, llamándose nodos hermanos o simplemente hermanos.

Es importante comprender la diferencia entre elementos y nodos de textos. Los elementos comúnmente son asociados a las etiquetas. En HTML todas las etiquetas son elementos, tales como <p>, <img> y <div> por lo que tienen atributos y contienes nodos hijos. Sin embargo, los nodos de textos no poseen atributos e hijos.

Siempre use el DOCTYPE correcto

El DOCTYPE (abreviado del inglés “document type declaration”, declaración del tipo de documento) informa cual versión de (X)HTML se usará para validar el documento; existen varios tipos a seleccionar. El DOCTYPE, debe aparecer siempre en la parte superior de cada página HTML y siendo un componente clave de las páginas web “obedientes” a los estándares.

En caso de usarse un DOCTYPE incompleto, no actualizado o simplemente no usarlo llevará al navegador a entrar en modo raro o extraño, donde el navegador asume que se ha programado fuera de los estándares.

Todavía todos los navegadores actuales no son capaces de procesar correctamente todos los tipos de documentos, sin embargo, muchos de ellos funcionan correctamente en los navegadores más utilizados actualmente, tales como:

HTML 4.01 Strict y Transitional, XHML 1.0 Strict y Transitional los se comportan del modo correcto en Internet Explorer (versión 6, 7 Beta), Mozilla y Opera 7. De ahora en adelante se adoptará para cada ejemplo HTML 4.01 Strict :

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> 

Resultando una única línea de código, o dos líneas con un salto de línea después de EN”.

La importancia de validar el HTML

Si los elementos son anidados de manera inadecuada pueden generarse problemas, véase la siguiente línea:

<p>Estos elementos han sido <strong>incorrectamente </p>anidados </strong>

El árbol que resulta de esto se encuentra incorrectamente anidado del todo, por tanto generará errores inesperados en los navegadores. Manteniendo su HTML válido se pueden evitar tales problemas.

Accediendo a los elementos

Afortunadamente, Javascript permite acceder a cada uno de los elementos de una página utilizando tan sólo algunos métodos y propiedades.

Si desea encontrar de manera rápida y fácil un elemento se tiene a la mano el método getElementById. El mismo permite un acceso inmediato a cualquier elemento tan sólo conociendo el valor de su atributo id. Véase el siguiente ejemplo:

<p>
<a id="contacto" href="contactos.html">Contáctenos</a>
</p>

Puede usarse el atributo id del elemento a para acceder al mismo:

var elementoContacto = document.getElementById("contacto");

Ahora el valor de la variable elementoContacto está referida al elemento [a] y cualquier operación sobre la misma afectará el hiperenlace.

El método getElementById es adecuado para operar sobre un elemento en específico, sin embargo, en ocasiones se necesita trabajar sobre un grupo de elementos por lo que en este caso puede utilizarse el método getElementsByTagName. Este retorna todos los elementos de un mismo tipo. Asumiendo la siguiente lista desordenada:

<ul>
<li><a href="editorial.html">Editorial</a></li>
<li><a href="semblanza.html">Autores</a></li>
<li><a href="noticias.html">Noticias</a></li>
<li><a href="contactos.html">Contátenos</a></li>
</ul>

Puede obtenerse todos los hipervínculos de la siguiente manera:

var hipervinculos= document.getElementsByTagName("a"); 

El valor de la variable hipervinculos es una colección de elementos [a]. Las colecciones son arreglos pudiéndose acceder a cada elemento a través de la ya conocida notación con corchetes.

Los elementos devueltos por getElementsByTagName serán ordenado según el orden que aparezcan en el código fuente. Por tanto para el caso anterior quedaría así:

  • hipervinculos[0] el elemento [a] para “Editorial”
  • hipervinculos[1] el elemento [a] para “Autores”
  • hipervinculos[2] el elemento [a] para “Noticias”
  • hipervinculos[3] el elemento [a] para “Contáctenos”

Otra maneras de acceder a un elemento usando su id es document.all["id"] la cual fue introducida en Internet Explorer 4 y document.layers["id"] introducida por Netscape 5 por que el W3C todavía no había estandarizado la manera de acceder a los elementos mediante su id. Sin embargo, no se recomienda su uso porque al estar fuera de los estándares actuales hay navegadores que no soportan estos métodos.

Por otro lado existen varios elementos en un documento HTML que pueden ser accedidos de otras maneras. El elemento body de un documento puede accederse a través de la forma document.body, mientras que el conjunto de todos los formularios en un documento puede encontrase en document.forms, así mismo el conjunto de todas las imágenes sería mediante document.images.

Actualmente la mayoría de los navegadores soportan esto métodos aún así es recomendable el uso del método getElementsByTagName, véase el siguiente ejemplo para acceder al elemento body:

var body = document.getElementsByTagName("body")[0]; 

Creando elementos y textos

La creación de nodos es posible mediante el uso de dos métodos disponibles en el objeto document. Dichos métodos son:

  • createElement(Tipo cadena): Crea un nuevo elemento del tipo especificado y devuelve un referencia a dicho elemento.
  • createTextNode(Cadena de texto): Crea un nuevo nodo de texto con el contenido especificado en la cadena de texto.

El siguiente ejemplo muestra cómo se crea un nuevo elemento de párrafo vacío:

var nuevoEnlace = document.createElement("a"); 

La variable nuevoEnlace ahora referencia un nuevo enlace listo para ser insertado en el documento. El texto que va dentro del elemento [a] es un nodo de texto hijo, por lo que debe ser creado por separado.

var nodoTexto = document.createTextNode("Semblanza");

Luego si desea modificar el nodo de texto ya existente, puede utilizarse la propiedad nodeValue, esta permite coger y poner el nodo de texto:

var textoViejo = nodoTexto.nodeValue;

nodoTexto.nodeValue = "Novedades"; 

El valor de la variable textoViejo es ahora "Semblanza" y el nuevo texto "Novedades". Se puede insertar un elemento o texto (nodo) como último hijo de un nodo ya existente usando el método appendChild. Este método coloca el nuevo nodo después de todos los hijos del nodo.

NuevoEnlace.appendChild(nodoTexto); 

Ahora todo lo que se necesita es insertar el enlace en el cuerpo del documento. Para hacer esto, se necesita una referencia al elemento body del documento, teniendo como guía los estándares siguientes:

var cuerpoRef = document.getElementsByTagName("body")[0]; 
cuerpoRef.appendChild(nuevoEnlace); 

Otra manera sería utilizando el método getElementById. Para ello se asume que la etiqueta <body> tiene asignado un valor para el atributo id.

<body id=”cuerpo”> 
var cuerpoRef = document.getElementById("cuerpo"); 
cuerpoRef.appendChild(nuevoEnlace); 

Existen básicamente tres maneras mediante las cuales un nuevo elemento o nodo de texto puede ser insertado en una página Web. Todo ello depende del punto en el cual se desee insertar el nuevo nodo: como último hijo de un elemento, antes de otro nodo o reemplazo para un nodo.

El caso de apertura de un nuevo hijo ya fue visto en el ejemplo anterior, luego para insertar el nodo antes de otro nodo se realiza utilizando el método insertBefore de su elemento padre, mientras que el reemplazo de nodo se utiliza el método replaceChild de su elemento padre.

Al usar insertBefore, se necesita tener referencias al nodo que va ser insertado y donde va a ser insertado, considérese el siguiente código HTML:

<p id="mwEnlaces">
<a id="editor" href="editorial.html">Editorial</a>
</p> 

Luego el nuevo enlace será insertado antes de enlace ya existente llamando el método insertBefore desde el nodo padre (párrafo):

var anclaTexto = document.createTextNode("Actualidad");
var nuevoAncla = document.createElement("a");
nuevoAncla.appendChild(anclaTexto);
var anclaExistente = document.getElementById("editor");
var padre = anclaExistente.parentNode;
var nuevoHijo = padre.insertBefore(nuevoAncla, anclaExistente); 

Si se hiciera una traducción del DOM hacia HTML después de esta operación el resultado sería el siguiente:

<p id="mwEnlaces">
<a> Actualidad </a><a id="editor" href="editorial.html">Editorial</a>
</p> 

En el caso de reemplazar el enlace usando replaceChild:

var nuevoHijo = padre.replaceChild(nuevoAncla, anclaExistente); 

El DOM lucirá así:

<p id="mwEnlaces">
<a> Actualidad </a>
</p>

Usando innerHTML

En aplicaciones complejas donde es necesario crear varios elementos a la vez, el código JavaScript generado puede ser extenso recurriéndose a la propiedad innerHTML. Dicha propiedad fue introducida por Microsoft permitiendo leer y escribir el contenido HTML de un elemento.

Por ejemplo, puede crearse fácilmente una tabla con múltiples celdas e insertarla luego en la página con innerHTML:

var tabla = '<table border="0">'; 
tabla += '<tr><td>Celda 1</td><td>Celda 2</td><td> Celda 3</td></tr>'; 
tabla += '</table>'; 
document.getElementById("datos").innerHTML = tabla; 

Eliminando un elemento o nodo de texto

Se pueden eliminar nodos existentes y nuevos. El método removeChild permite eliminar nodos hijos a cualquier nodo con tan sólo pasarle las referencias del nodo hijo [a] eliminar y su correspondiente padre. Para mejor compresión retómese el ejemplo anterior:

<p id="mwEnlaces">
<a id="editor" href="editorial.html">Editorial</a>
</p> 

El método removeChild será usado para eliminar el hipervínculo del elemento padre párrafo:

var ancla = document.getElementById("editor");
var padre = ancla.parentNode;
var hijoRemovido = padre.removeChild(ancla);

La variable hijoRemovido todavía hace referencia al elemento, de manera que fue removido pero no destruido, no pudiéndose localizar en ninguna parte del DOM. Este se encuentra disponible en memoria como si fuera creado usando el método createElement. Esto permite posicionarlo en cualquier otra parte de la página.

Lectura y escritura de los atributos de un elemento

Las partes más frecuentemente usadas de un elemento HTML son sus atributos, tales como: id, class, href., title, estilos CSS, entre muchas otras piezas de información que pueden se incluidas en una etiqueta HTML.

Los atributos de una etiqueta son traducidos por el navegador en propiedades de un objeto. Dos métodos existen para leer y escribir los atributos de un elemento, getAttribute permite leer el valor de un atributo mientras que setAttribute permite su escritura.

En ocasiones se hace necesario ver las propiedades y métodos de un determinado elemento, esto puede realizarse mediante la siguiente función utilitaria:

function inspector(el) {
var str =””;
for (var i in el){
str+=I + “: ” + el.getAttribute(i) + “\n”; 
}
alert(str);
} 

Para usar la función inspector() tan sólo debe pasarle la referencia al elemento, continuando con el ejemplo anterior resulta:

var ancla = document.getElementById("editor");

inspector(ancla); 

Para modificar el atributo title del hipervínculo, elemento referenciado por la variable ancla, se usará el setAttribute, pasándole el nombre del atributo y el valor:

var ancla = document.getElementById("editor");
ancla.setAttribute("title", "Artículos de programación");
var nuevoTitulo = ancla.getAttribute("title"); 
El valor de la variable nuevoTitulo es ahora “Artículos de programación”.

Manipulando los estilos de los elementos

Como se ha visto, los atributos que le son asignados a las etiquetas HTML están disponibles como propiedades de sus correspondientes nodos en el DOM. Las propiedades de estilo pueden ser aplicadas a través del DOM.

Cada atributo CSS posee una propiedad del DOM equivalente, formándose con el mismo nombre del atributo CSS pero sin los guiones y llevando la primera letra de las palabras a mayúsculas. Véase el siguiente ejemplo para mayor entendimiento donde se utiliza un atributo CSS modelo:

algun-atributo-css 

Tendrá como equivalente la siguiente propiedad o método en Javascript:

algunAtributoCss

Por tanto, para cambiar el atributo CSS font-family de un elemento, podría realizarse de lo siguiente:

ancla.style.fontFamily = 'sans-serif'; 

Los valores CSS en Javascript serán en su mayoría del tipo cadena; por ejemplo: font-size, pues posee dimensiones tales como “px”, “%”. Sólo los atributos completamente numéricos, tales como z-index serán del tipo entero.

En muchos casos es necesario aparecer y desaparecer un determinado elemento, para ellos se utiliza el atributo CSS display, por ejemplo, para desaparecer:

ancla.style.display = 'none'; 

Luego para volverlo a mostrar se le asigna otro valor:

ancla.style.display = 'inline'; 

Ejemplo: Adjuntar múltiples ficheros a la vez

Este es el primer ejemplo completo donde se propone utilizar la manipulación del DOM mediante javascript con el objetivo de adicionar tantos elementos input del tipo file como tantos ficheros se deseen subir al servidor.

Se muestra una versión simplificada del problema limitada tan sólo al lado del cliente. Para ello es necesario imaginarse un sistema en línea donde se suben ficheros al servidor, ejemplo de ello podría ser una aplicación de correo electrónico.

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Ejemplo para adjuntar múltiples ficheros</title>
<script language="javascript" type="text/javascript">
function nuevoFichero() {
var input = document.getElementsByTagName("input")[0];
var nuevoInput = input.cloneNode(true);
input.parentNode.appendChild(nuevoInput);
}
</script>
</head> 
<body>
<form action="upload.php" method="post" enctype="multipart/form-data">
<fieldset><legend>Adjuntar múltiples ficheros</legend>
<input name="ficheros[]" type="file" size="60" >
</fieldset>
<a href="javascript: nuevoFichero();">Adjuntar otro fichero</a>   
<input name="Subir" type="submit" value="Adjuntar" >
</form>
</body>
</html>

El enlace para adjuntar otro fichero hace una llamada a la función nuevoFichero() realizándose las siguientes tareas en la misma:

Se accede al primer elemento input encontrado en el documento:

var input = document.getElementsByTagName("input")[0]; 
  • Se crea un nuevo elemento input referenciado por la variable nuevoInput utilizando el método cloneNode resultando un elemento idéntico al primero:
var nuevoInput = input.cloneNode(true); 
  • Aquí se accede al nodo padre del elemento input mediante el método parentNode y la vez se inserta un nuevo elemento hijo copia del primero por medio del método appendChild:
input.parentNode.appendChild(nuevoInput); 

Es de notar que en este ejemplo podría haberse usado el evento clic para la llamada de la función, si embargo se dejó reservado para cuando el tema de la manipulación de eventos del DOM sea abordado.

Ejemplo: Mostrador de diapositivas

Este ejemplo contempla un mostrador de diapositivas (en este caso imágenes). Se tienen dos enlaces, uno mostrar la diapositiva siguiente y otro para mostrar la diapositiva anterior. Una posible aplicación práctica podría ser en una galería de fotos.

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"

"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Ejemplo de un mostrador de diapositivas</title> 
<script type="text/javascript">
var contador = 0;
var diapositivas = [];
function inicio() {
var contenedor = document.getElementById("diapositivas");
while (contenedor.childNodes.length > 0){
if (contenedor.getElementsByTagName("img")[0]==contenedor.firstChild){
diapositivas[diapositivas.length] = contenedor.removeChild(contenedor .firstChild);
}
else contenedor.removeChild(contenedor.firstChild);
}
}
function adelante() {
contador++;
if (contador >= diapositivas.length) contador = 0;
ponerImagen()
}
function atras() {
contador--;
if (contador < 0 ) contador = diapositivas.length - 1;
ponerImagen();
} 
function ponerImagen() {
var contenedor = document.getElementById("diapositivas");
if (contenedor.childNodes.length==0)
contenedor.appendChild(diapositivas[contador]);
else contenedor.replaceChild(diapositivas[contador], contenedor.childNodes[0]);
}
</script>
</head>
<body>
<a href="javascript: atras();">Atrás</a>
<span id="diapositivas">
<img src="diapositiva1.jpg" alt="Diapositiva 1" height="100" width="200">
<img src="diapositiva2.jpg" alt="Diapositiva 2" height="100" width="200">
<img src="diapositiva3.jpg" alt="Diapositiva 3" height="100" width="200">
</span>
<a href="javascript: adelante();">Adelante</a>
</body>
</html>
<script type="text/javascript">
inicio();
ponerImagen();
</script> 

Este ejemplo se analizará de manera abreviada dejando un análisis detallado por parte del lector. Se tiene dos variables globales, contador y diapositivas, la primera para indicar la diapositiva que se está mostrando actualmente y la última para almacenar en un arreglo el conjunto de diapositivas a mostrar.

La función inicio() remueve todos elementos img y los almacena. Es de notar que todo nodo hijo del nodo con id diapositivas parásito (ejemplo: salto de línea) es eliminado para evitar un mal funcionamiento posterior.

Por otro lado la función ponerImagen() se encarga de colocar la imagen apuntada por el contador en el contenedor de diapositivas. Mientras que las funciones atras() y adelante() se encargan de decrementar e incrementar el contador respectivamente.

Conclusión

El presente material tan sólo ha sido una ligera aproximación a la manipulación del Modelo de Objetos del Documento. Explorando el DOM se puede encontrar, cambiar, adicionar y eliminar elementos de un documento. Es sin duda alguna una poderosa técnica para generar aplicaciones Web dinámicas en el lado del cliente.