[JavaScript][AVANZADO] Trabajar con DOM desde Firefox e Internet Explorer

Iniciado por arthusu, Julio 16, 2011, 07:05:12 PM

Tema anterior - Siguiente tema

0 Miembros y 1 Visitante están viendo este tema.

Julio 16, 2011, 07:05:12 PM Ultima modificación: Septiembre 07, 2013, 04:55:56 PM por Destructor.php


Para un proyecto del trabajo me veo submergido en la creación dinámica de el contenido de una página web. Para realizar este trabajo, me decanté a trabajar con DOM accediendo via Javascript. Sé que existen librerías muy completas crossbrowser que realizan fácilmente el trabajo requerido, soltando código compatible para (casi) todos los navegadores, pero yo soy de ésos que prefiere jugar a un nivel más bajo (siempre que no se me compliquen las cosas en exceso).

Sabiendo que Firefox respeta mejor los estándares que Internet Explorer, llevo ya la costumbre de desarrollar para Firefox y luego adecuar el código para que funcione para Internet Explorer... pero esta vez la cosa se me ha ido de las manos...

He encontrado varios problemas relacionados con las diferencias en que el DOM es tratado por ambos navegadores, con lo que he podido observar con mis propios ojos que no siempre Firefox respeta el estándar o que la forma de trabajar de Internet Explorer acaba siendo muy sui-generis.

En este artículo presento un ejemplo básico: una página con un DIV que sirve como base para que un código Javascript monte una tabla "modelo" mediante DOM, para ilustrar las diferencias que he encontrado.

Introducción

Primero de todo, deberíamos empezar por generar nuestra página base que va a soportar toda la mecánica de DOM / Javascript que vamos a montar. Voy a proponer una estructura sencilla:
Código: html5

    <html>
    <head>
    <title>Ejemplo de DOM</title>
    </head>
    <body>
    <span>Comenzar</span>
    <div id="enlace"></div>
    </body>
    </html>


Ok, con ésto tenemos ya una página normal válida con un div que nos servirá como enlace, es decir, todo lo que hagamos lo añadiremos a este DIV. Además, tenemos un SPAN que usaremos como botón para comenzar el proceso.

Vale, empecemos. Necesitamos teclear Javascript, así que necesitamos crear un espacio para él. Lo vamos a meter dentro del HEAD, justo debajo del TITLE:
Código: html5

    <head>
    <title>Ejemplo de DOM</title>
    <script language="JavaScript">
     
    </script>
    </head>


Entonces, vamos a hacer que el SPAN sea el evento que empiece todo el proceso. Para ello, le pondremos unos atributos y algun estilo para que se nos presente como un link:

Código: html5
    <span style="cursor:pointer; text-decoration: underline;" onclick="javascript:dibujarTabla();">Comenzar</span>


Con ello tendremos que el texto "Comenzar" estará subrayado y al poner el ratón encima el puntero cambiará al famoso dedo. Además, le hemos dicho que cuando éste sea clicado ejecute una función Javascript llamada "dibujarTabla". Así que ahora tenemos que escribir una función dentro del espacio Javascript que hemos creado en el HEAD. Yo siempre empiezo con un alert() para comprobar que de momento todo funciona. Luego lo quitaremos, eh?:

Código: javascript
   function dibujarTabla()
    {
    alert('Parece que funciona');
    }


Al ejecutar la página, si clicamos en el texto subrayado nos saldrá una ventanita de diálogo con el texto "Parece que funciona" y un botón "Aceptar" que al clicarlo cerrará el diálogo. Ok. Hasta aquí todo bién. Ya tenemos el esqueleto funcionando. Ahora ya podemos empezar con el ejemplo de DOM.

Creación de la tabla vía DOM.

Para empezar, qué tiene una tabla de toda la vida? Pues un tag TABLE que encierra varios tags TR que son las filas, y cada TR encierra varios tags TD que son las columnas. Pues vamos allá. Eliminamos el alert() y tecleamos las instrucciones del DOM para la creación de la tabla. Para los que no estén acostumbrados al DOM, diré (así por encima) que se toma una página HTML como un XML, es decir, un conjunto de elementos que contienen hijos y éstos a su vez contienen otros hijos. Así que la construcción de una tabla se basa en la declaración de el padre, luego el hijo, luego el hijo lo añadimos al padre, y así sucesivamente:

   
Código: javascript
 function dibujarTabla()
    {
    TABLA = document.createElement('table');
    FILA = document.creaateElement('tr');
    COLUMNA1 = document.createElement('td');
    TEXTO1 = document.createTextNode('celda 1');
    COLUMNA1.appendChild(TEXTO1);
    COLUMNA2 = document.createElement('td');
    TEXTO2 = document.createTextNode('celda 2');
    COLUMNA2.appendChild(TEXTO2);
    FILA.appendChild(COLUMNA1);
    FILA.appendChild(COLUMNA2);
    TABLA.appendChild(FILA);
    document.getElementById('enlace').appendChild(TABLA);
    }


Hemos hecho lo siguiente: primero creamos el elemento tabla, luego el elemento fila, luego un elemento columna1, luego un elemento de tipo texto (con algo de texto dentro), y ligamos el elemento de texto a la columna. Luego hacemos otra columna repitiendo los pasos de la columna. Luego hemos ligado las columnas a la fila, luego la fila a la tabla y luego (y aquí está la madre del cordero) la tabla al DIV que habíamos llamado "enlace".

Si ahora nos vamos al Firefox veremos que al darle al link "Comenzar" nos genera una tabla de dos columnas justo debajo (en realidad lo crea donde esté situado el DIV). Muy bién. Y si lo probamos en el Internet Explorer? Pues no funciona!!!

Cómo podemos hacer para que funcione? Pues después de mucho rebuscar me encontré con que las especificaciones del objeto de tabla, en el W3C, mandan unos elementos agrupadores llamados THEAD, TBODY y TFOOT. A la hora de parsear una página normal parece que no son necesarios, pero la implementación de Internet Explorer requiere obligatoriamente que un elemento de éstos esté creado y funcionando entre el elemento TABLE y el elemento TR. Consulta las especificaciones del elemento en la página del W3C para más info.

Total, no sé si se debe a que la implementación de Internet Explorer está mal hecha no permitiendo escribir tablas via DOM sin TBODY o que el Firefox peca de elástico permitiendo algo que la W3C no recomienda, pero el caso es que sin un tag de éstos en Internet Explorer no funciona. Así que deberemos modificar la creación de la tabla para contemplar éste tag:

   
Código: javascript
 function dibujarTabla()
    {
    TABLA = document.createElement('table');
    TBODY = document.createElement('tbody');
    FILA = document.creaateElement('tr');
    COLUMNA1 = document.createElement('td');
    TEXTO1 = document.createTextNode('celda 1');
    COLUMNA1.appendChild(TEXTO1);
    COLUMNA2 = document.createElement('td');
    TEXTO2 = document.createTextNode('celda 2');
    COLUMNA2.appendChild(TEXTO2);
    FILA.appendChild(COLUMNA1);
    FILA.appendChild(COLUMNA2);
    TBODY.appendChild(FILA);
    TABLA.appendChild(TBODY);
    document.getElementById('enlace').appendChild(TABLA);
    }


Ahora ya tenemos éste código compatible con Internet Explorer y Firefox. El código de la página quedaría de la siguiente forma:

Código: html5
    <html>
    <head>
    <title>Ejemplo de DOM</title>
    <script language="JavaScript">
    function dibujarTabla()
    {
    TABLA = document.createElement('table');
    TBODY = document.createElement('tbody');
    FILA = document.creaateElement('tr');
    COLUMNA1 = document.createElement('td');
    TEXTO1 = document.createTextNode('celda 1');
    COLUMNA1.appendChild(TEXTO1);
    COLUMNA2 = document.createElement('td');
    TEXTO2 = document.createTextNode('celda 2');
    COLUMNA2.appendChild(TEXTO2);
    FILA.appendChild(COLUMNA1);
    FILA.appendChild(COLUMNA2);
    TBODY.appendChild(FILA);
    TABLA.appendChild(TBODY);
    document.getElementById('enlace').appendChild(TABLA);
    }
    </script>
    </head>
    <body>
    <span style="cursor:pointer; text-decoration: underline;" onclick="javascript:dibujarTabla();">Comenzar</span>
    <div id="enlace"></div>
    </body>
    </html>


Añadiendo CSS

Para los que trabajamos con HTML amenudo estamos acostumbrados definir páginas de estilos que especifiquen los conjuntos de colores, fuentes y estilos varios en vez de irlos declarando en cada elemento. Es una forma de trabajar reconocida y aconsejada, además que realmente resulta muy práctica a la larga, pues te ahora mucho tiempo en las modificaciones típicas a posteriori.

Pero trabajar con clases CSS también comporta problemas cuando necesitamos que el DOM las defina en ambos navegadores. Pero no corramos tanto... vamos a definir una clase simple de estilo y vamos a intentar agregarla a nuestro script. Para ello debemos abrir un espacio dentro del HEAD que nos permita definir clases de estilos. Y allí crearemos una clase "negrita" y el estilo que queremos que tengan todos los elementos que adquieran de ésta clase:

   
Código: html5
 <style>
    .negrita
    {
    font-weight: bold;
    }
    </style>


Ok, ya tenemos la clase definida. Usando el sentido común, sabremos que para definir una clase a un elemento le debemos agregar a ese elemento un atributo llamado class e igualarlo al nombre de la classe CSS que queramos. Así que si el atributo se llama class, deberíamos hacer algo así:
Código: javascript

COLUMNA1.setAttribute('class','negrita');


Vale, vale, voy a poner todo el trozo javascript para que no nos liemos:

   
Código: javascript
function dibujarTabla()
    {
    TABLA = document.createElement('table');
    TBODY = document.createElement('tbody');
    FILA = document.creaateElement('tr');
    COLUMNA1 = document.createElement('td');
    COLUMNA1.setAttribute('class','negrita');
    TEXTO1 = document.createTextNode('celda 1');
    COLUMNA1.appendChild(TEXTO1);
    COLUMNA2 = document.createElement('td');
    COLUMNA2.setAttribute('class','negrita');
    TEXTO2 = document.createTextNode('celda 2');
    COLUMNA2.appendChild(TEXTO2);
    FILA.appendChild(COLUMNA1);
    FILA.appendChild(COLUMNA2);
    TBODY.appendChild(FILA);
    TABLA.appendChild(TBODY);
    document.getElementById('enlace').appendChild(TABLA);
    }


Ahora que ya lo tenemos agregado, vamos al Firefox y ejecutamos: funciona perfecto ;) Entonces, vamos al Internet Explorer y... no hay estilo!! Cómo? mmmm... Qué toca ahora? Googlear. Finalmente encuentro una página dónde, a parte del problema anterior, se presenta este mismo problema con los CSS. La explicación que se da es que el Internet Explorer mapea la instrucción

Citarelemento.setAttribute(nombre,valor)

por

Citarelemento[nombre]=valor

Además, como class es una palabra reservada en Javascript, se mapea className en vez de class para el atributo class. Así que tenemos que cambiar la construcción del setAttribute:

Código: javascript
   function dibujarTabla()
    {
    TABLA = document.createElement('table');
    TBODY = document.createElement('tbody');
    FILA = document.creaateElement('tr');
    COLUMNA1 = document.createElement('td');
    COLUMNA1.setAttribute('className','negrita');
    TEXTO1 = document.createTextNode('celda 1');
    COLUMNA1.appendChild(TEXTO1);
    COLUMNA2 = document.createElement('td');
    COLUMNA2.setAttribute('className','negrita');
    TEXTO2 = document.createTextNode('celda 2');
    COLUMNA2.appendChild(TEXTO2);
    FILA.appendChild(COLUMNA1);
    FILA.appendChild(COLUMNA2);
    TBODY.appendChild(FILA);
    TABLA.appendChild(TBODY);
    document.getElementById('enlace').appendChild(TABLA);
    }


Vamos a verificar que en Internet Explorer funciona. Ok, ahora ya sale el texto en negrita. Y sólo por curiosidad... vamos a probarlo en Firefox... Ahora no sale en Firefox!! Cagundena! Qué podemos hacer?? Pues hay dos posibilidades. La primera es liarnos la manta a la cabeza y distinguir qué navegador es el que está ejecutando el código, y para cada navegador realizar una cosa u otra. Ésta es una buena solución pero a yo intento evitarlo siempre que no es absolutamente necesario. Encuentro bastante barroco un código simple cuando se empieza a llenar de condicionales de si es un navegador u otro. La otra opción, no tan elegante pero completamente funcional, es definir las dos órdenes, la que funciona para Firefox y la que funciona para Internet Explorer. La orden que un navegador no entienda la va a obviar, así que adelante:

   
Código: javascript
 function dibujarTabla()
    {
    TABLA = document.createElement('table');
    TBODY = document.createElement('tbody');
    FILA = document.creaateElement('tr');
    COLUMNA1 = document.createElement('td');
    COLUMNA1.setAttribute('className','negrita');
    COLUMNA1.setAttribute('class','negrita');
    TEXTO1 = document.createTextNode('celda 1');
    COLUMNA1.appendChild(TEXTO1);
    COLUMNA2 = document.createElement('td');
    COLUMNA2.setAttribute('className','negrita');
    COLUMNA2.setAttribute('class','negrita');
    TEXTO2 = document.createTextNode('celda 2');
    COLUMNA2.appendChild(TEXTO2);
    FILA.appendChild(COLUMNA1);
    FILA.appendChild(COLUMNA2);
    TBODY.appendChild(FILA);
    TABLA.appendChild(TBODY);
    document.getElementById('enlace').appendChild(TABLA);
    }


Así que tenemos el archivo final de la siguiente manera:

   
Código: html5
 <html>
    <head>
    <title>Ejemplo de DOM</title>
    <script language="JavaScript">
    function dibujarTabla()
    {
    TABLA = document.createElement('table');
    TBODY = document.createElement('tbody');
    FILA = document.creaateElement('tr');
    COLUMNA1 = document.createElement('td');
    COLUMNA1.setAttribute('className','negrita');
    COLUMNA1.setAttribute('class','negrita');
    TEXTO1 = document.createTextNode('celda 1');
    COLUMNA1.appendChild(TEXTO1);
    COLUMNA2 = document.createElement('td');
    COLUMNA2.setAttribute('className','negrita');
    COLUMNA2.setAttribute('class','negrita');
    TEXTO2 = document.createTextNode('celda 2');
    COLUMNA2.appendChild(TEXTO2);
    FILA.appendChild(COLUMNA1);
    FILA.appendChild(COLUMNA2);
    TBODY.appendChild(FILA);
    TABLA.appendChild(TBODY);
    document.getElementById('enlace').appendChild(TABLA);
    }
    </script>
    <style>
    .negrita
    {
    font-weight: bold;
    }
    </style>
    </head>
    <body>
    <span style="cursor:pointer; text-decoration: underline;" onclick="javascript:dibujarTabla();">Comenzar</span>
    <div id="enlace"></div>
    </body>
    </html>


Setando atributos a los elementos

Seguimos con la gama de problemas. Hasta ahora, el único atributo que hemos setado es el referente a las clases CSS. Pero existen varios más, como el colspan de una celda por ejemplo. El sentido común nos dicta que si los atributos se setan con .setAttribute, la construcción normal para un colspan sería:

CitarCOLUMNA3.setAttribute('colspan','2');

Pues bién, en Firefox funciona perfectamente pero en Internet Explorer no (para variar). Cómo lo hacemos para Internet Explorer? Pues de la siguiente manera:

CitarCOLUMNA3.colSpan=2;

que funcionará tanto para Internet Explorer como para Firefox. Prestad atención a la 'S' mayúscula del colSpan.

Pero, en cambio, hay otros atributos que se setan perfectamente en ambos navegadores usando la sintaxi .setAttribute('atributo', 'valor'), como el size de un INPUT de texto o su value. Increible pero cierto. Conclusión? En principio se debería usar la construcción .setAttribute, y si se ve que no funciona, probar con la sintaxi .atributo ... es lo que hay...

Más cosas. Resulta que Internet Explorer hay una limitación sobre el atributo name en los INPUTs, tal como describe la MSDN:

CitarThe NAME attribute cannot be set at run time on elements dynamically created with the createElement method. To create an element with a name attribute, include the attribute and value when using the createElement method.

Esta cuestión está ámpliamente discutida en No tienes permitido ver los links. Registrarse o Entrar a mi cuenta y No tienes permitido ver los links. Registrarse o Entrar a mi cuenta (seguid también el hilo de los comentarios) y proponen usar una función que intente setar el nombre al estilo del Internet Explorer y si falla que lo sete al estilo de Firefox, para poder cumplir ambos requerimientos:

Código: javascript
    function createElementWithName(type, name) {
    var element;
    // First try the IE way; if this fails then use the standard way
    if (document.all) {
    element =
    document.createElement('<'+type+' name="'+name+'" />');
    } else {
    element = document.createElement(type);
    element.setAttribute('name', name);
    }
    return element;
    }



Setar eventos a los elementos


Los eventos tampoco se libran. Si se quieren usar los eventos, en Firefox podemos seguir la tónica habitual:

Citarelemento.setAttribute("onclick","javascript:mi_funcion('"+param_texto+"',"+param_numero+")");

Pero claro, en Internet Explorer tampoco funciona... En Ovillo plantean una posible solución, pero no me ha funcionado. Personalmente, ahora mismo me estoy peleando con ésto. Si encuentro alguna solución la postearé.

Fuente: No tienes permitido ver los links. Registrarse o Entrar a mi cuenta
Pentest - Hacking & Security Services

Contact me: No tienes permitido ver los links. Registrarse o Entrar a mi cuenta