DOM 1 Document Object Model INTRODUCCIÓN Es un conjunto de utilidades específicamente diseñadas para manipular documentos XML. Por extensión, DOM también se puede utilizar para manipular documentos XHTML y HTML. Técnicamente, DOM es una API de funciones que se pueden utilizar para manipular las páginas XHTML de forma rápida y eficiente. La primera especificación de DOM (DOM Level 1) permitió homogeneizar la implementación del DHTML o HTML dinámico en los diferentes navegadores, ya que permitía modificar el contenido de las páginas web sin necesidad de recargar la página entera. El responsable del DOM es el consorcio W3C (World Wide Web Consortium). http://www.w3.org/DOM/ Especificación del Modelo de Objetos del Documento (DOM), Nivel 1, en español: http://html.conclase.net/w3c/dom1-es/cover.html 2 VERSIONES La primera vez que el DOM se utilizó, fue con el navegador Netscape en su versión 2.0 Este DOM se conoce también como el modelo básico, o el DOM.Nivel 0. DOM.Nivel 1 (1998), en la cual se consideraron las características y manipulación de todos los elementos existentes en los archivos HTML y XML. DOM.Nivel 2 (2000), en esta especificación se incluyó la manipulación de eventos en el navegador, la capacidad de interacción con CSS, y la manipulación de partes del texto en las páginas de la web. DOM.Nivel 3 (2004), utiliza la DTD (Definición del tipo de documento) y la validación de documentos. 3 TIPOS DE NODOS Los documentos XML y HTML tratados por DOM se convierten en una jerarquía de nodos. Los nodos que representan los documentos pueden ser de diferentes tipos. Document: es el nodo raíz de todos los documentos HTML y XML. Todos los demás nodos derivan de él. DocumentType: es el nodo que contiene la representación del DTD empleado en la página (indicado mediante el DOCTYPE). Element: representa el contenido definido por un par de etiquetas de apertura y cierre (<etiqueta>...</etiqueta>) o de una etiqueta abreviada que se abre y se cierra a la vez (<etiqueta/>). Es el único nodo que puede tener tanto nodos hijos como atributos. Attr: representa el par nombre-de-atributo/valor. Text: almacena el contenido del texto que se encuentra entre una etiqueta de apertura y una de cierre. También almacena el contenido de una sección de tipo CDATA. CDataSection: es el nodo que representa una sección de tipo <![CDATA[ ]]>. Comment: representa un comentario de XML. 4 EJEMPLO <?xml version="1.0"?> <clientes> <!-- El primer cliente --> <cliente> <nombre>Empresa SA</nombre> <sector>Tecnologia</sector> <notas><![CDATA[ Llamar la proxima semana ]]></notas> </cliente> </clientes> 5 INTERFAZ NODE – CONSTANTES El objeto Node define las siguientes constantes para la identificación de los distintos tipos de nodos: Node.ELEMENT_NODE = 1 Node.ATTRIBUTE_NODE = 2 Node.TEXT_NODE = 3 Node.CDATA_SECTION_NODE = 4 Node.ENTITY_REFERENCE_NODE = 5 Node.ENTITY_NODE = 6 Node.PROCESSING_INSTRUCTION_NODE = 7 Node.COMMENT_NODE = 8 Node.DOCUMENT_NODE = 9 Node.DOCUMENT_TYPE_NODE = 10 Node.DOCUMENT_FRAGMENT_NODE = 11 Node.NOTATION_NODE = 12 6 INTERFAZ NODE – PROPIEDADES Y MÉTODOS (I) Propiedad/Método Valor devuelto Descripción nodeName String El nombre del nodo (no está definido para algunos tipos de nodo) nodeValue String El valor del nodo (no está definido para algunos tipos de nodo) nodeType Number Una de las 12 constantes definidas anteriormente ownerDocument Document firstChild Node Referencia del primer nodo de la lista childNodes lastChild Node Referencia del último nodo de la lista childNodes childNodes NodeList Lista de todos los nodos hijo del nodo actual previousSibling Node Referencia del nodo hermano anterior o null si este nodo es el primer hermano Referencia del documento al que pertenece el nodo 7 INTERFAZ NODE – PROPIEDADES Y MÉTODOS (II) Propiedad/Método Valor devuelto Descripción nextSibling Node Referencia del nodo hermano siguiente o null si este nodo es el último hermano hasChildNodes() Bolean Devuelve true si el nodo actual tiene uno o más nodos hijo attributes NamedNodeMap Se emplea con nodos de tipo Element. Contiene objetos de tipo Attr que definen todos los atributos del elemento appendChild(nodo) Node Añade un nuevo nodo al final de la lista childNodes removeChild(nodo) Node Elimina un nodo de la lista childNodes replaceChild(nuevoNodo, anteriorNodo) Node Reemplaza el nodo anteriorNodo por el nodo nuevoNodo insertBefore(nuevoNodo, anteriorNodo) Node Inserta el nodo nuevoNodo antes que la posición del nodo anteriorNodo dentro de la lista childNodes 8 HTML Y DOM Firefox y Safari implementan DOM de nivel 1 y 2 (y parte del 3), otros navegadores como Internet Explorer (versión 7 y anteriores) ni siquiera son capaces de ofrecer una implementación completa de DOM nivel 1. Los métodos y propiedades incluidas en la tabla anterior son específicos de XML, aunque pueden aplicarse a todos los lenguajes basados en XML, como por ejemplo XHTML. Para las páginas creadas con HTML, los navegadores hacen como si HTML estuviera basado en XML y lo tratan de la misma forma. No obstante, se han definido algunas extensiones y particularidades específicas para XHTML y HTML. Cuando se utiliza DOM en páginas HTML, el nodo raíz de todos los demás se define en el objeto HTMLDocument. Además, se crean objetos de tipo HTMLElement por cada nodo de tipo Element del árbol DOM. El objeto document es parte del BOM (Browser Object Model), aunque también se considera que es equivalente del objeto Document del DOM de los documentos XML. Por este motivo, el objeto document también hace referencia al nodo raíz de todas las páginas HTML. 9 HTML DOM Las modificaciones en el árbol de nodos DOM sólo se pueden realizar cuando toda la página web se ha cargado en el navegador. El motivo es que los navegadores construyen el árbol de nodos DOM una vez que se ha cargado completamente la página web. Cuando una página no ha terminado de cargarse, su árbol no está construido y por tanto no se pueden utilizar las funciones DOM. Si una página realiza modificaciones automáticas (sin intervención del usuario) es importante utilizar el evento onload() para llamar a las funciones de JavaScript. 10 ACCESO A NODOS Obtener el elemento raíz de la página: var objeto_html = document.documentElement; (<html>) Obtener el elemento <head> y <body>: var objeto_head = objeto_html.firsChild; var objeto_body = objeto_html.lastChild; var objeto_head = objeto_html.childNodes[0]; var objeto_body = objeto_html.childNodes[1]; Obtener el elemento directamente: var objeto_body = document.body; Ejemplos de propiedades: objeto_head.parentNode == objeto_html objeto_body.parentNode == objeto_html objeto_body.previousSibling == objeto_head objeto_head.nextSibling == objeto_body objeto_head.ownerDocument == document var numeroDescendientes = objeto_html.childNodes.length; 11 TIPOS DE NODOS alert(document.nodeType); // 9 alert(document.documentElement.nodeType); // 1 alert(document.nodeType == Node.DOCUMENT_NODE); // true; en IE fallaria alert(document.documentElement.nodeType == Node.ELEMENT_NODE); // true; en IE fallaria El único navegador que no soporta las constantes predefinidas es Internet Explorer 7 y sus versiones anteriores, por tanto es necesario definirlas: if(typeof Node == "undefined") { var Node = { ELEMENT_NODE: 1, ATTRIBUTE_NODE: 2, TEXT_NODE: 3, CDATA_SECTION_NODE: 4, ENTITY_REFERENCE_NODE: 5, ENTITY_NODE: 6, PROCESSING_INSTRUCTION_NODE: 7, COMMENT_NODE: 8, DOCUMENT_NODE: 9, DOCUMENT_TYPE_NODE: 10, DOCUMENT_FRAGMENT_NODE: 11, NOTATION_NODE: 12 }; } 12 ACCESO A ATRIBUTOS Métodos DOM, los nodos de tipo Element contienen la propiedad attributes, que permite acceder a todos los atributos de cada elemento: getNamedItem(nombre), devuelve el nodo cuya propiedad nodeName contenga el valor nombre. removeNamedItem(nombre), elimina el nodo cuya propiedad nodeName coincida con el valor nombre. setNamedItem(nodo), añade el nodo a la lista attributes, indexándolo según su propiedad nodeName. item(posicion), devuelve el nodo que se encuentra en la posición indicada por el valor numérico posición. Los métodos anteriores devuelven un nodo de tipo Attr. Métodos DOM directos: getAttribute(nombre), es equivalente a attributes.getNamedItem(nombre). setAttribute(nombre, valor) equivalente a attributes.getNamedItem(nombre).value= valor. removeAttribute(nombre), equivalente a attributes.removeNamedItem(nombre). 13 ACCESO A ATRIBUTOS – EJEMPLO <p id="introduccion" style="color: blue">Párrafo de prueba</p> var p = document.getElementById("introduccion"); var elId = p.attributes.getNamedItem("id").nodeValue; //elId = "introduccion" var elId = p.attributes.item(0).nodeValue; // elId = "introduccion" p.attributes.getNamedItem("id").nodeValue = "preintroduccion"; var atributo = document.createAttribute("lang"); atributo.nodeValue = "es"; p.attributes.setNamedItem(atributo); var p = document.getElementById("introduccion"); var elId = p.getAttribute("id"); // elId = "introduccion" p.setAttribute("id", "preintroduccion"); 14 ACCESO DIRECTO A LOS NODOS El árbol DOM de una pagina xhtml puede tener miles de nodos, por lo que DOM proporciona una serie de métodos para acceder de forma directa: getElementsByTagName(): obtiene todos los elementos de la página cuya etiqueta sea igual que el parámetro que se le pasa a la función. El valor devuelto es un objeto de tipo NodeList (array con todos los nodos que cumplen la condición). var parrafos = document.getElementsByTagName(“p”); var primerParrafo = parrafos[0]; var parrafoUno = document. getElementsByTagName(“p”)[0]; var enlaces = primerParrafo.getElementsByTabName(“a”); 15 ACCESO DIRECTO A LOS NODOS getElementsByName(): obtiene todos los elementos de la página cuyo atributo name coincida con el parámetro que se le pasa a la función. <p name=“pintroduccion”> esta es la introducción </p> var parrafointro = document.getElementsByName(“pintroduccion”); getElementById(): obtiene el elemento cuyo atributo id coincide con el parámetro indicado en la función. El atributo id debe ser único para cada elemento de una misma página. var cabecera = document.getElementById(“cabecera”); <div id=“cabecera”> <p>… <img src=…. </div> 16 CREAR, MODIFICAR Y ELIMINAR NODOS createAttribute(nombre): Crea un nodo de tipo atributo con el nombre indicado. createCDataSection(texto): Crea una sección CDATA con un nodo hijo de tipo texto que contiene el valor indicado. createComment(texto): Crea un nodo de tipo comentario que contiene el valor indicado. createDocumentFragment(): Crea un nodo de tipo DocumentFragment. createElement(nombre_etiqueta): Crea un elemento del tipo indicado en el parámetro nombre_etiqueta. createEntityReference(nombre): Crea un nodo de tipo EntityReference createProcessingInstruction(objetivo, datos): Crea un nodo de tipo ProcessingInstruction. createTextNode(texto): Crea un nodo de tipo texto con el valor indicado como parámetro. Internet Explorer no soporta los métodos createCDataSection, createEntityReference y createProcessingInstruction. Los métodos más 17 empleados son createElement, createTextNode y createAttribute. EJEMPLO Añadir un párrafo: var pnuevo = document.createElement(“p”); var texto = document.createTextNode(“texto del párrafo”); pnuevo.appendChild(texto); document.body.appendChild(pnuevo); var pviejo = document.body.getElementById(“pcabecera”); document.body.insertBefore(pnuevo,pviejo); Eliminar un párrafo: var pcab = document.getElementById(“pcabecera”); document.body.removeChild(pcab); //sino conozco de donde cuelga este elemento pcab.parentNode.removeChild(pcab); Intercambiar un párrafo: var pviejo = document.body.getElementById(“pcabecera”); pviejo.parentNode.replaceChild(pnuevo, pviejo); 18 ATRIBUTOS HTML EN DOM Dado el siguiente elemento: <img id=“logo” src=“logo.gif”> var laImagen = document.getElementById(“logo”); DOM tradicional: var origen = laImagen.attributes.getNamedItem(“src”); laImagen.attributes.getNamedItem(“src").nodeValue = " nuevologo.gif "; DOM tradicional directo: var origen = laImagen.getAttribute(“src”); laImagen.setAttribute(“src”,”nuevologo.gif”); DOM para HTML: var origen = laImagen.src; laImagen.src = “nuevologo.gif”; 19 PROPIEDADES CSS EN DOM (I) Atributo class (palabra reservada en JavaScript): <p id="parrafo" class="normal">...</p> var parrafo = document.getElementById("parrafo"); alert(parrafo.class); // muestra "undefined" alert(parrafo.className); // muestra "normal“ Propiedades CSS mediante el atributo style de HTML: <p id="parrafo" style=“font-size:18px;color:red">...</p> var parrafo = document.getElementById("parrafo"); var color = parrafo.style.color; //propiedades con nombre compuesto, eliminamos guiones y escribimos en mayúsculas la primera letra de cada palabra que no sea la primera var tamanyo = parrafo.style.fontSize; parrafo.style.fontSize = “10px”; 20 PROPIEDADES CSS EN DOM (II) Para obtener la propiedad independientemente de cómo se haya aplicado, style o mediantes reglas(en los elementos html, con una clase de estilo o con un identificador único). var parrafo = document.getElementById("parrafo"); // Código JavaScript para Internet Explorer // currentStyle requiere el nombre según el formato JavaScript var elfontsize = parrafo.currentStyle[„fontSize‟]; // Código JavaScript para otros navegadores //getPropertyValue requiere el nombre original de la propiedad var elfontsize = document.defaultView.getComputedStyle(parrafo, “ ”) .getPropertyValue(„font-size‟); 21 PROPIEDADES CSS EN DOM (III) Función compatible con todos los navegadores, creada por el programador Robert Nyman y publicada en su blog personal (http://www.robertnyman.com/2006/04/24/get-the-renderedstyle-of-an-element/) // Código JavaScript para cualquier navegador var parrafo = document.getElementById("parrafo"); var color = getStyle(parrafo, 'color'); 22 PROPIEDAD INNERHTML (I) innerHTML no es un estándar, es una propiedad de Microsoft (http://msdn2.microsoft.com/en-us/library/ms533897.aspx). Formará parte del estándar HTML 5. Una de las diferencias principales entre innerHTML y DOM es que el primero es un string y el segundo una estructura de objetos jerárquica (un arbol). Crear un elemento innerHTML document.getElementById("contenedor").innerHTML = "<div id="capa" >Texto<!--comentario--></div>"; DOM div = document.createElement("div"); div.setAttribute("id", "capa"); texto = document.createTextNode("Texto"); div.appendChild(texto); div_comentario = document.createComment("comentario"); div.appendChild(div_comentario); document.getElementById("contenedor").appendChild(div); 23 PROPIEDAD INNERHTML (II) Obtener el texto de un elemento innerHTML texto = document.getElementById("contenedor").innerHTML DOM texto = document.getElementById("contenedor").firstChild.nodeValue; Comparativa http://www.quirksmode.org/dom/innerhtml.html 24 CROSS BROWSER Las diferencias existentes entre los navegadores disponibles en la actualidad complican en exceso el desarrollo de aplicaciones compatibles con todos los navegadores, llamadas aplicaciones "cross browser" en inglés. Hay que diseñar utilidades para abstraernos del navegador. Ejemplo: http://www.cross-browser.com http://books.google.es/books?id=f_2R9ra2PjEC&lpg=P P1&pg=PA292#v=onepage&q=&f=false 25 ENLACES DOM Mozilla https://developer.mozilla.org/en/Gecko_DOM_Reference DOM IE http://msdn.microsoft.com/en-us/library/ms533053(VS.85).aspx WebKit: Chrome, Safari http://webkit.org/projects/dom/index.html 26