Curso AJAX Y DOM 2007 Marcos A. San Martín Calera Septiembre 2007 TEMA 3 A: INTRODUCCIÓN AL DOM 1.- ¿Qué es el DOM?...................................................................................................... 2 1.1.- DOM y JavaScript ............................................................................................... 2 1.3.-¿Cómo se accede al DOM?................................................................................. 3 2. Instalar una consola.................................................................................................... 4 3. Averiguar la versión DOM de Explorer ....................................................................... 5 4.- El DOM de XML en Firefox........................................................................................ 6 5.- XML en plataformas cruzadas................................................................................... 7 6.- Carga de un documento XML (o html) ...................................................................... 8 1 Curso AJAX Y DOM 2007 Marcos A. San Martín Calera Septiembre 2007 1.- ¿Qué es el DOM? (http://developer.mozilla.org/es/docs/Referencia_DOM_de_Gecko:Introducci%C3%B3n) El modelo de objeto de documento (DOM) es una interfaz de programación para los documentos HTML y XML. Facilita una representación estructurada del documento y define de qué manera los programas pueden acceder, al fin de modificar, tanto su estructura, estilo y contenido. El DOM da una representación del documento como un grupo de nodos y objetos estructurados que tienen propiedades y métodos. Esencialmente, conecta las páginas web a scripts o lenguajes de programación. Una página web es un documento. Éste documento puede exhibirse en la ventana de un navegador o también como código fuente HTML. Pero, en los dos casos, es el mismo documento. El modelo de objeto de documento (DOM) proporciona otras formas de presentar, guarda y manipular este mismo documento. El DOM es una representación completamente orientada al objeto de la página web y puede ser modificado con un lenguaje de script como JavaScript. El W3C DOM estándar forma la base del funcionamiento del DOM en muchos navegadores modernos. Varios navegadores ofrecen extensiones más allá del estándar W3C, hay que ir con extremo cuidado al utilizarlas en la web, ya que los documentos pueden ser consultados por navegadores que tienen DOMs diferentes. Por ejemplo, el DOM de W3C especifica que el método getElementsByTagName en el código de abajo debe devolver una lista de todos los elementos <P> del documento: paragraphs = document.getElementsByTagName("P"); // paragraphs[0] es el primer elemento <p> // paragraphs[1] es el segundo elemento <p>, etc. alert(paragraphs[0].nodeName); Todas las propiedades, métodos y eventos disponibles para la manipulación y la creación de páginas web está organizado dentro de objetos. Un ejemplo: el objeto document representa al documento mismo, el objeto table hace funcionar la interfaz especial HTMLTableElement del DOM para acceder a tablas HTML, y así sucesivamente. 1.1.- DOM y JavaScript El ejemplo de abajo es JavaScript. Pero utiliza el DOM para acceder al documento y a sus elementos. El DOM no es un lenguaje de programación pero sin él, el lenguaje JavaScript no tiene ningún modelo o noción de las páginas web, de la páginas XML ni de los elementos con los cuales es usualmente relacionado. Cada elemento -"el documento íntegro, el título, las tablas dentro del documento, los títulos de las tablas, el texto dentro de las celdas de las tablas"- es parte del modelo de objeto del documento para cada documento, así se puede acceder y manipularlos utilizando el DOM y un lenguaje de escritura, como JavaScript. El contenido de la página es almacenado en DOM y el acceso y la manipulación se hace vía JavaScript, podría representarse aproximadamente así: API(web o página XML) = DOM + JS(lenguaje de script) El DOM fue diseñado para ser independiente de cualquier lenguaje de programación 2 Curso AJAX Y DOM 2007 Marcos A. San Martín Calera Septiembre 2007 particular, hace que la presentación estructural del documento sea disponible desde un simple y consistente API. 1.3.-¿Cómo se accede al DOM? Los diferentes navegadores tienen directrices DOM distintas, y éstas directrices tienen diversos grados de conformidad al actual estándar DOM, pero todos los navegadores web usan el modelo de objeto de documento para hacer accesibles las páginas web al script. Cuando se crea un script –esté en un elemento <SCRIPT> o incluido en una página web por la instrucción de cargar un script– inmediatamente está disponible para usarlo con el API, accediendo así a los elementos documento o ventana, para manipular el documento mismo o sus diferentes partes, las cuales son los varios elementos de una página web. La programación DOM hace algo tan simple como lo siguiente, lo cual abre un mensaje de alerta usando la función alert() desde el objeto ventana, o permite métodos DOM más sofisticados para crear realmente un nuevo contenido, como en el largo ejemplo de más abajo. <body onload="window.alert('Hola clase');"> El ejemplo siguiente muestra la función a ejecutar cuando el documento se está cargando (y que el DOM completo es disponible para su uso). Esta función crea un nuevo elemento H1, le pone texto y después lo agrega al árbol del documento: <html> <head> <script> // ejecuta esta función cuando la página se carga window.onload = function() { // crea un par de elementos // en otra página HTML vacía heading = document.createElement("h1"); heading_text = document.createTextNode("Cabeza grande!"); heading.appendChild(heading_text); document.body.appendChild(heading); } </script> </head> <body> </body> </html> 3 Curso AJAX Y DOM 2007 Marcos A. San Martín Calera Septiembre 2007 2. Instalar una consola Dada las características del curso mejor que usar la función Alert de Javascript vamos a crear una consola por la que obtener los mensajes. Con esto evitaremos la molesta detención de la ejecución del código cada vez que queremos observar que está haciendo nuestro script. De momento este código será un ejemplo de DOM. Más adelante irá apareciendo el significado de cada instrucción. Función de Javascript para consola: function consola(txt){ var con = document.getElementById("consola"); con.innerHTML += txt + "<hr> "; } Simplemente hemos hecho referencia a un elemento cuyo Id es “consola”. Por ello necesitamos que el código tenga esta línea: <div id="consola"></div> La siguiente instrucción hace que el contenido del elemento encontrado (un DIV) sea HTML y que sea el que tenía más el que le indiquemos. Hemos puesto como separador una línea. Para probar: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>Untitled</title> <script> function consola(txt){ var con = document.getElementById("consola"); con.innerHTML += txt + "<hr> "; } </script> </head> <body onLoad="consola('hola mundo')"> <div id="consola"></div> </body> </html> Nota: Para que funcione la función primero se debe haber cargado el documento, sino no encontrará la etiqueta <DIV>. 4 Curso AJAX Y DOM 2007 Marcos A. San Martín Calera Septiembre 2007 3. Averiguar la versión DOM de Explorer Para crear un objeto ActiveX en JavaScript, Microsoft implemento una nueva clase llamada ActiveXObject que puede utilizarse para instanciar cualquier número de objetos ActiveX. Su constructor acepta un argumento, una cadena con la versión del objeto ActiveX que se desea crear. En ese caso, es la versión del documento XML. El primer objeto ActiveX del modelo DOM de XML se denominó Microsoft. XmlDom y se creaba así: var oXmlDom = new ActiveXObject("Microsoft.XmlDom"); Los objetos DOM de XML más recientes se comportan como cualquier otro objeto DOM, permitiéndonos recorrer el árbol del modelo DOM y manipular nodos DOM. Dado que existen 6 versiones diferentes siempre desearemos utilizar la más reciente, es muy útil emplear una función para determinar qué versión podemos usar. Haciéndolo, nos aseguramos de disponer del soporte más actualizado y del mejor rendimiento. function creaDocumento(){ //Creo array de versiones var aVersion = "MSXML2.DOMDocument.6.0", "MSXML2.DOMDocument.5.0", "MSXML2.DOMDocument.4.0","MSXML2.DOMDocument.3.0", "MSXMI.2.DOMDocument", "Microsoft.XmlDom"]; //Recorro en búsqueda for (var i =0;i < aVersion.length; i++) { try { var oXmlDom = new ActiveXObject (aVersion[i]); consola(aVersion[i]); return oXmlDom; } catch (oError){ consola(oError.description + " " + oError); } } throw new Error("MSXML no está instalado"); } Esta función recorre toda la matriz aVersion que contiene las cadenas de versión de los distintos documentos DOM MSXML. Empieza el recorrido con la versión más reciente, DOMDocument.6.0, e intenta (try) crear el documento DOM. Si la creación del objeto tiene éxito, se devuelve y creaDocumento() existe. Además escribe el nombre de la versión creada en la consola. Si falla (catch) , se produce un error que se depura en el bloque try..catch, de forma que el bucle continúa. En este caso se escribe en la consola la descripción del error. Como el tratamiento es diferente en Explorer y Mozilla se apaña con una suma de ambas interpretaciones para que se pueda leer cual es el error. Si falla la creación del documento MSXML, se genera un error por Javascript (en caso de Mozilla, claro) indicando que MSXML no está instalado. 5 Curso AJAX Y DOM 2007 Marcos A. San Martín Calera Septiembre 2007 Para que funciones debemos hacer algo como: function enCarga() { var oXlmDom = creaDocumento(); } En la práctica sólo comprobamos si soporta la versión 4, en caso contrario tomamos Microsoft.XmlDom 4.- El DOM de XML en Firefox Cuando llega la hora de implementar el DOM de XML en Mozilla Firefox, los desarrolladores adoptan un método de aproximación más centrado en los estándares convirtiéndolo en parte de una implementación de JavaScript. Así, Mozilla asegura el soporte DOM de XML en todas las plataformas de todos los navegadores basados en Gecko. Para crear un DOM de XML en Firefox, debemos llamar al método createDocument () correspondiente al objeto document. implementation. Este método acepta tres argumentos: 1. Una cadena que contiene el espacio de nombres URI para el documento que se desea utilizar 2. Es una cadena que contiene el nombre cualificado del elemento raíz del documento 3. El tipo de documento (también conocido como doctype) que deseamos crear. Actualmente no hay soporte JavaScript para tipos de documentos en Firefox, de forma que el tercer argumento debería ser siempre null. Los nombres de los espacios de nombres XML pueden aparecer como nombres cualificados, que contienen un símbolo de dos puntos (:) que divide al nombre en un prefijo del espacio de nombres y una parte local. El prefijo, que corresponde a la referencia URI, selecciona un espacio de nombres. La combinación del espacio de nombres URI gestionado universalmente y del espacio de nombres propio del documento produce identificadores que son únicos a nivel universal. Se proporcionan mecanismos para definir el ámbito de los prefijos y los valores por defecto. Para crear un documento DOM vacío, podemos hacer lo siguiente: var oXmlDom = document.implementation.createDocument(“”,””,null); Para crear un DOM de XML con un elemento de documento, especifique el nombre de la etiqueta en el segundo argumento: var oXmlDom = document.implementation.createDocument(“”,”libros”,null); Este código crea un DOM de XML cuyo documentElement es <libros/>. 6 Curso AJAX Y DOM 2007 Marcos A. San Martín Calera Septiembre 2007 Especificando el espacio de nombres URI en el primer argumento: var oXmlDom = document.implementation.createDocument ("http://www.site.com", "libros", null) Cuando especificamos un espacio de nombres en el método createDocument (), Firefox asígna automáticamente el prefijo 0 para representar el espacio de nombres URL <a0:libros xmlns:a0="http: //www.site.com" /> 5.- XML en plataformas cruzadas (http://www.w3schools.com/dom/dom_parser.asp) Siempre es necesario tener en cuenta las diferencias del código Javascript entre las plataformas cruzadas. Para ello diseñamos esta función de Javascript. <html> <head> <script type="text/javascript"> var xmlDoc; var navegador; function cargaDocumento() { // code for IE if (window.ActiveXObject){ xmlDoc=new ActiveXObject("Microsoft.XMLDOM"); navegador = “IE”; consola("IE") } // code for Mozilla, Firefox, Opera, etc. else if (document.implementation && document.implementation.createDocument) { xmlDoc=document.implementation.createDocument("","",null); navegador = “FI”; consola("Mozilla, Firefox u Opera") // Error } else { consola("Este navegador no soporta DOM"); } consola(xmlDoc); } function consola(txt){ var con = document.getElementById("consola"); con.innerHTML += txt + "<br> "; } </script> </head> <body onLoad="cargaDocumento()"> <div id="consola"></div> </body> </html> 7 Curso AJAX Y DOM 2007 Marcos A. San Martín Calera Septiembre 2007 En este ejemplo aparecerá en la consola el navegador que estamos usando. En caso de que el navegador soporte ActiveXObject escribirá IE, en caso de que soporte document.implementation y este permita crear documentos estamos ante Firefox y en caso de que no lo soporte entonces pintará un error. Aprovechamos para guardar en la variable “navegador” de que navegador se trata. 6.- Carga de un documento XML (o html) Ambos navegadores soportan el método de cargar XML con load() que se puede combinar con el método ASYNC. LOAD() Æ Permite cargar una un archivo XML en una localización específica en la Web. ASYNC Æ Como sucede con XMLHttp (AJAX), load () nos permite cargar los datos en dos modos: asíncrono y sincrónico. Por defecto, el método load () es asíncrono. Para utilizar el modo sincrónico, debemos configurar async del objeto MSXML como false. function cargaXML(dir){ cargaDocumento(); xmlDoc.async = false; try {xmlDoc.load(dir);} catch (oError) {consola(oError.description + oError);return false; } var raiz = xmlDoc.documentElement; consola(texto(raiz)) } Creamos esta función que intenta cargar la dirección indicada. Si falla capturamos el error y lo mostramos en la consola. Si no falla mostramos lo que vale xmlDoc. Para mostrar el contenido del XML cargado hemos recurrido a una función que recorre todo el árbol XML y que recoge cada valor de cada nodo con el fin de que sea compatible en ambos navegadores. Adelantándonos en el tema (luego se explica cada método) usaremos este visor de contenido de lo que cargamos: function texto(oNodo){ var tt = ""; for (var i=0; i<oNodo.childNodes.length; i++){ if(oNodo.childNodes[i].hasChildNodes()) { tt += texto(oNodo.childNodes[i]) } else {tt += oNodo.childNodes[i].nodeValue;} } return tt; } EJERCICIOS: ¿Qué ocurre si probamos un XML local? 1.- En Firefox 2.- En iExplorer 8 Curso AJAX Y DOM 2007 Marcos A. San Martín Calera Septiembre 2007 <platos> <primeros> <nombre>Macarrones</nombre> <fecha>Sun, 30 Dec 1899 00:00:00 +0100</fecha> </primeros> <primeros> <nombre>Paella</nombre> <fecha>Fri, 30 Dec 2004 00:00:00 +0100</fecha> </primeros> </platos> <body onLoad="cargaXML(‘carga_xml.xml’)";> ¿Qué ocurre si probamos un RSS (que es XML) de un dominio externo al que cargamos esta función? <body onLoad="cargaXML('http://www.boua.ua.es/rss.asp')";> 3.- Prueba desde un dominio (127.0.0.1) ¿qué ocurre con Firefox? ¿Qué ocurre con iExplorer? 4.- Prueba desde una dirección de disco (G:\curso_dom_ajax_xml\ tema3\ejercicios\carga_xml.html) ¿qué ocurre con Firefox? ¿y con iExplorer? 5.- ¿Qué ocurre si probamos con un XML mal formado / no válido? <platos> <primeros> <nombre>Macarrones</nombre> <fecha>Sun, 30 Dec 1899 00:00:00 +0100</fecha> </primeros> <primeros> <nombre>Paella</nombre> <fecha>Fri, 30 Dec 2004 00:00:00 +0100</fecha> </primeros> </kk> Cuando trabajamos en modo asíncrono ASYNC= true entonces el script continua aunque el XML no se haya cargado (igual que ocurre con AJAX). Para controlar si se ha terminado de cargar necesitamos el evento de onreadystatechange que permite monotorizar la propiedad readyState. Por desgracia es solo de Explorer. El Firefox usa el evento Load y el manejador de envento onload. Cruzando ambos navegadores saldría: 9 Curso AJAX Y DOM 2007 Marcos A. San Martín Calera Septiembre 2007 function cargaXML(dir){ cargaDocumento(); xmlDoc.async = true; xmlDoc.load(dir); if (navegador==”IE”){ xmlDoc.onreadystatechange = function(){ if (xmlDoc.readyState == 4){ var raiz = xmlDoc.documentElement; consola(texto(raiz)); }else { consola(xmlDoc.readyState + " sentado esperado"); }; } }else{ xmlDoc.onload = function(){ var raiz = xmlDoc.documentElement; consola(texto(raiz)); } } } Lo cual es un lío: mientras que iExplorer si espera a que se cargue la página (gracias a readyState que se me te en un bucle igual que hacía AJAX), Gecko no. Es por eso que evitaremos usar el DOM de esta manera. Concretamente, en este ejemplo, en la parte que se refiere a Explorer: xmlDoc.onreadystatechange = function(){ if (xmlDoc.readyState == 4){ var raiz = xmlDoc.documentElement; consola(texto(raiz)); }else { consola(xmlDoc.readyState + "sentado esperado"); }; } El documento XML que se indica en la variable dir (por ejemplo “rss.xml”) se carga en el DOM de XML. Cuando readyState alcanza el valor 4, significa que el documento se ha cargado por completo y se ejecuta el código que se encuentra dentro del bloque if. LOADXML (loadXML) Æ solo iExplorer La segunda forma de cargar datos XML, loadXML (), difiere del método load () en que se carga XML desde una cadena. Esta cadena debe contener código XML bien formado, como en el siguiente ejemplo: var sXml = "<root><persona><nombre>Pepe Pepe Oreja Repe</nombre></persona></root>"; XmlDoc.loadXML(sXml); Aquí, los datos XML contenidos en la variable sXml se cargan en el documento XmlDoc. No hay ninguna razón para comprobar la propiedad readyState o para configurar la propiedad async cuando empleamos loadXML(), ya que no implica ninguna petición al servidor. El método loadXML () no existe en la implementación en Firefox. Sin embargo, es posible emular su comportamiento mediante la clase DOMParser. DOMParser dispone de un método llamado parseFromString (), que carga una cadena y la inicializa en un documento: 10 Curso AJAX Y DOM 2007 Marcos A. San Martín Calera Septiembre 2007 var oParser = new DOMParser(); xmlDoc = oParser.parseFromString(txt,"text/xml"); El método parseFromString () devuelve un objeto DOM de XML, de forma que podemos tratar XmlDoc en este código como uno de estos objetos. El código cruzado quedaría: function cargaTxtXML(){ cargaDocumento(); if(navegador=="IE") xmlDoc.loadXML(txt); else { var oParser = new DOMParser(); xmlDoc = oParser.parseFromString(txt,"text/xml"); } var raiz = xmlDoc.documentElement; consola(texto(raiz)); }; 11