5.- INCOMPATIBILIDADES. CÓMO HACER PÁGINAS COMPATIBLES.

El HTML dinámico puede mejorar enormemente el aspecto e interactividad de las páginas web. Sin embargo, los desarrolladores nos encontramos con el problema de la compatibilidad entre navegadores y de las características nuevas que cada navegador aporta y que los otros no las soportan... y las diferencias entre navegadores antiguos y nuevos (versiones 4.xx).

Por supuesto, entre Netscape Navigator 4.xx y Microsoft Explorer 4.xx hay grandes diferencias, tal y como las hay entre éstos y otros navegadores antiguos:

    • IE 4.xx permite el acceso dinámico a todas las propiedades CSS, con lo cual un elemento HTML puede expandirse o cambiar de color cuando el cursor se mueve sobre él, mientras que en NN 4.xx sólo se pueden cambiar las propiedades de visibilidad, posición, color de fondo y clipping.
    • Aunque IE 4.xx ofrece control sobre todas las propiedades CSS, la cantidad de errores, tanto en la documentación como en la implementación, es numerosa, por lo que realmente ese soporte no lo es total.

La idea de crear sitios separados, uno por navegador, es impracticable (que no imposible. Como veremos, será el último recurso) para casi cualquier sitio que tenga varias páginas. Es más importante desarrollar técnicas para hacer páginas compatibles que funcionen en todos los navegadores, aunque las mismas páginas ofrezcan más o menos prestaciones dependiendo del navegador en que estemos visualizando los documentos.



5.1.- DETECTAR EL NOMBRE Y VERSIÓN DEL NAVEGADOR.

Determinar el nombre y versión del navegador nos será sumamente útil, ya que de este modo podremos decidir si hay que hacer o no cierta acción y cómo, o si hay que colocar o no ciertos contenidos.

Una forma de determinar el navegador es la siguiente:

var esNN4 = (navigator.appName=='Netscape'  &&
parseInt(navigator.appVersion>=4);
      
Una vez asignado el valor a esNN4, podemos utilizar esa variable para distinguir entre un navegador de Netscape, versión 4, y el resto de navegadores (IEx, NN3.x, etc).

Ahora bien, eso no es suficiente, ya que tratamos al E4 como un navegador antiguo, desaprovechando sus capacidades para soporte de DHTML. Debemos hacer más comprobaciones para decidir qué tipo de navegador y qué versión estamos utilizando.

Ver ejemplo de detección del tipo de navegador aquí.




5.2.- DETERMINAR EL D.O.M. SOPORTADO POR EL NAVEGADOR.

La segunda forma de determinar el navegador es detectar el Modelo de Objetos del Documento que soporta. Así, lo que haremos en el código JS es acceder a las propiedades de los elementos de la página a través del DOM soportado por el navegador.

Esto lo podemos hacer del siguiente modo:

if( document.layers ){
    alert("Navegador NN4");
    //acceder a los elementos mediante el DOM del N4
}

if( document.all ){
    alert("Navegador IE4");
    //acceder a los elementos mediante el DOM del E4
}
De este modo, sólo si soporta el DOM del N4 o del E4 se realian esas acciones. En otro caso no se hace nada.






5.3.- SALVAR LAS INCOMPATIBILIDADES DEL D.O.M.

El método anterior funciona muy bien, ya que sólo se hace algo (y ese algo es específico de cada navegador) si el navegador es moderno y va a soportar ese código. Sin embargo es un engorro estar siempre colocando las dos comprobaciones en cada sitio donde se planteé una posible incompatibilidad entre navegadores.

Una buena solución a ese problema es la siguiente:

if( document.layers ){
    esNN4=1;
}else{
    esNN4=0;
}

var ref= (esNN4) ? document.layer1 : document.all.layer1.style; 
      
Haciendo esto, tenemos que ref es igual a document.layer1 si el navegador es el N4, o bien igual a document.all.layer1.style si es el E4. En este caso, la suposición de que si no es el N4 será el E4 sigue siendo peligrosa, por lo que es posible que debamos hacer más comprobaciones para evitar errores de JS en navegadores antiguos.

Otra cosa a tener en cuenta es que este tipo de referencias que estamos viendo no se deben hacer hasta que la página está completamente cargada, con lo que tendremos todos los elementos definidos, ya que de lo contrario, si intentamos definir la variable antes, obtendremos un mensaje de error "bloque no definido".

Para evitar esto, debemos llamar a una función inicio() (una vez cargada toda la pagina) desde el tag <BODY>, en donde se definen las variables y se asignan como hemos indicado antes:

.
.
.
function inicio() {
    // inicializamos las variables
    // como hemos visto
}
.
.
.
<BODY onLoad="inicio();">
.
.
.





5.4.- EVALUAR EN CADA MOMENTO.

La última forma de escribir código que funcione en ambos navegadores será evaluar en cada momento una expresión, utilizando la función eval(), de forma que en dicha evaluación se establezca la expresión correcta, de acuerdo al DOM soportado, para acceder a las propiedades o realizar la acción sobre algún elemento de página.

Esto podríamos hacerlo del siguiente modo:

if( document.layers ){
    conj = "document.layers" ;
    estilo="" ;
}else{
    conj = "document.all" ;
    estilo=".style" ;
}
....
....
eval( conj + '["elemento"]' + estilo + '.left += 10 ;' );
      
De esa forma, conseguimos que la evaluación nos dé la expresión correcta para el DOM utilizado. Así, si es el N4, eso es equivalente a haber puesto:
document.layers["elemento"].left += 10 ;
      
y si fuese el E4, sería equivalente a haber puesto:
document.all["elemento"].style.left += 10 ;
      





5.5.- COMENTARIOS CONDICIONALES.

Los comentarios condicionales son muy útiles para crear contenido específico siempre que se cumpla cierta condición. Son soportados sólo por N4.

La sintaxis es la siguiente: utilizamos un comentario normal HTML, y justo tras el símbolo de apertura, colocamos un ampersand (&) y encerrado entre llaves la condición que se debe cumplir. Tras las llaves se coloca un punto-y-coma (;).

<!--&{ typeof document.layers == "object" };
    <LAYER ID=layer1>
        Contenido a visualizar en el layer1.
    </LAYER>
-->
      
Si la condición es evaluada como verdadera, la marca de comentario es ignorada y el contenido del comentario pasa a ser parte de la página. Si la condición es evaluada como falsa, el comentario será un comentario más, y por lo tanto será ignorado.

Los navegadores que no soportan comentarios condicionales, simplemente los ignoran.




5.6.- <LAYER>, <ILAYER>, <NOLAYER>

En otro tema hablamos acerca del tag <LAYER> y del <ILAYER> para crear layers con posición absoluta o relativa. Ahora vamos a comentar el uso del tag <NOLAYER>, para añadir contenido a páginas que serán visualizadas en navegadores que no soportan el tag <LAYER>.

Por supuesto, N4 ignorará cualquier contenido entre <NOLAYER> y /NOLAYER. Esto nos ofrece la posibilidad de añadir contenido alternativo que será visualizado sólo por navegadores antiguos en la misma página y junto al mismo código que será visualizado por navegadores modernos.

Esto viene a ser lo mismo que pasa con <FRAME> y <NOFRAMES>: los navegadores que no soportan frames, ignoran lo que hay entre los tags <FRAME> y </FRAME>, y visualizan lo que hay entre los tags <NOFRAMES> y </NOFRAMES>. Por otro lado, los navegadores que si soportan frames, visualizan lo que hay entre los tags <FRAME> y </FRAME>, e ignoran lo que hay entre los tags <NOFRAMES> y </NOFRAMES>.

En el siguiente ejemplo definimos un layer que incluye otra página mediante el atributo SRC y justo después, una alternativa para ser visualizada en navegadores que no soportan el <LAYER>, introducida con <NOLAYER> :

<LAYER ID=layer1 SRC="introDHTML.html">
</LAYER>

<NOLAYER>
    <IMG SRC=="alternativa.gif">
</NOLAYER>
      
De esta forma, el N4 visualiza la página introDHTML.html en el layer1, mientras que el resto de navegadores visualizará en su lugar la imagen alternativa.gif ya que ignoran tanto el tag LAYER como el NOLAYER.




5.7.- COMPATIBILIDAD HACIA ATRÁS.

Hasta ahora hemos visto cómo acceder a las propiedades mediante el DOM, salvando las diferencias entre N4 y E4, y también cómo crear contenidos que fuesen visualizables tanto en N4 como en E4. Sin embargo, algunas de las formas de resolver las incompatibilidades vistas hasta ahora sólo sirven en los navegadores nuevos, y en los antiguos (versiones 3.x) podrían dar problemas.

En este punto veremos técnicas para conseguir que el contenido de ciertas páginas sea visto (de una forma u otra) en todos los navegadores:

  1. No procesar lo no reconocido:

    Ya hemos apuntado anteriormente el uso del tag <NOLAYER> para incluir código alternativo que pueda ser visualizado en navegadores antiguos. Esto se basa en las especificaciones HTML, de acuerdo con las cuales, se ignorará todo aquello (tags o atributos) que no sean reconocido. Por eso, cuando hacemos una página y nos equivocamos al definir el contenido, el navegador no da errores (salvo de JS), pero posiblemente no veremos lo que esperábamos.

    Haciendo uso de esto, tenemos muchas posibilidades para definir contenidos que, más o menos, serán vistos en todos los navegadores.

  2. Orden de declaración de los bloques de contenido:

    Debemos tener mucho cuidado a la hora de definir elementos con posición absoluta en la página, ya que si lo hacemos, ese contenido puede ser visto en todos los navegadores de forma correcta, y al tiempo en las versiones nuevas disfrutaremos de las ventajas del DHTML.

    Ver ejemplo de declaracion de bloques de contenido: Luna.

    El siguiente ejemplo usa DHTML para visualizar varios items en la misma posición en la página HTML (uno cada vez). En navegadores antiguos, el atributo position de la propiedad STYLE es ignorado, con lo cual los items son todos vistos al mismo tiempo, pero uno debajo de otro, a modo de menú:

    <DIV ID=i1 STYLE="position:absolute; left:50px; top:20px; 
     width:300px;">
        <H1>Item 1</H1>
        texto relacionado con el item 1.
    </DIV>
    
    <DIV ID=i2 STYLE="position:absolute; left:50px; top:20px; 
     width:300px;">
        <H1>Item 2</H1>
        texto relacionado con el item 2.
    </DIV>
    
    <DIV ID=i3 STYLE="position:absolute; left:50px; top:20px; 
     width:300px;">
        <H1>Item 3</H1>
        texto relacionado con el item 3.
    </DIV>
          

  3. Crear contenido sólo para versiones 4 mediante JavaScript:

    A veces querremos que cierto contenido aparezca sólo si es un navegador nuevo, con lo cual la técnica anterior no nos vale, ya que todo se visualiza en todos los navegadores. El modo de hacer esto es mediante un script.

    El script comprueba el tipo de navegador que está siendo usado y escribe el contenido apropiado para cada uno.

    En el siguiente ejemplo, tenemos un layer que se expande o contrae ante eventos del ratón (el código JS no lo escribo aquí, pero es simplemente modificar la propiedad visibility del layer a visible o invisible). Sin embargo, con la técnica anterior, el layer sería visto desde el principio en navegadores antiguos, cosa que no nos interesa.

    <A href="#"
      onMouseOver="expandir();"
      onMouseOver="contraer();">
        <IMG SRC="barraMenu.gif">
    </A>
    
    <DIV ID=i1 STYLE="visibility: hidden;">
        item 1 <br>
        item 2 <br>
        item 3 <br>
        item 4 <br>
    </DIV>
          
    La alternativa es escribir el código HTML sólo si es un navegador moderno, para ello hacemos un script que escriba directamente mediante document.write() el código HTML que aparecerá, sólo si es un navegador nuevo:
    <A href="#"
      onMouseOver="expandir();"
      onMouseOver="contraer();">
        <IMG SRC="barraMenu.gif">
    </A>
    
    <DIV ID=i1 STYLE="visibility: hidden;">
        <SCRIPT>
          if( navigator.appVersion.indexOf("4") != -1 ) {
            document.writeln("item 1 <br>");
            document.writeln("item 2 <br>");
            document.writeln("item 3 <br>");
            document.writeln("item 4 <br>");
          }
        </SCRIPT>
    </DIV>
          

  4. Usar la versión de JavaScript en el tag <SCRIPT> :

    Debido a que al lenguaje JS cada vez se le añaden nuevas posibilidades, y para evitar que navegadores antiguos tengan problemas con código JS moderno, cada nueva versión del lenguaje tiene un número de versión.

    Utilizando el atributo LANGUAGE del tag <SCRIPT> podemos especificar la versión del lenguaje, lo que hará que sólo sea ejecutado dicho código por navegadores que soporten esa versión del lenguaje.

    Por ejemplo, el código insertado en

    <SCRIPT LANGUAGE="JavaScript1.2">
        ....
        ....
    </SCRIPT>
          
    será ejecutado por el N4, pero no por el N3.

    Utilizando la versión del lenguaje, podemos desarrollar rutinas que utilicen todas las características nuevas, sin tener que preocuparnos de los posibles errores que nos puedan dar los navegadores antiguos. Así, podemos tener diferentes niveles de funcionalidad dependiendo del navegador del usuario.

    Hay que tener cuidado con IE4, ya que Microsoft tiene su propio lenguaje de script, llamado JScript, y aunque ha aumentado su funcionalidad, no soporta todas las características de JavaScript1.2 . Sin embargo el problema viene debido a que aún así, E4 leerá el código que coloquemos en <SCRIPT LANGUAGE="JavaScript1.2">

    Es por esto que deberemos tener mucho cuidado, y a veces, incluso hacer comprobaciones de nombre de navegador y versión de éste, además de utilizar la versión del lenguaje.

    El siguiente codigo pertenece a lo que podría ser una barra de menú que se expande/contrae. En los navegadores que reconocen la versión 1.2, la barra se debería expandir y contraer para dejar ver las cabeceras y producir un efecto de rotación. En los que reconocen hasta la versi&oaucte;n 1.1, la barra sólo produciría el efecto de rotación. Y en los que sólo soportan JS básico o no lo soportan, la barra sería estática:

    <SCRIPT LANGUAGE="JavaScript">
        //version para los que soportan JavaScript basico
    
        function expandir(barra){}
    
        function contraer(barra){}
    </SCRIPT>
    
    <SCRIPT LANGUAGE="JavaScript1.1">
        //version para los que soportan hasta la version 1.1
    
        function expandir(barra){
           eval("document."+barra+".src = "+barra+"ON.gif");
        }
    
        function contraer(barra){
           eval("document."+barra+".src = "+barra+"OFF.gif");
        }
    </SCRIPT>
    
    <SCRIPT LANGUAGE="JavaScript1.2">
        //version para los que soportan hasta la version 1.2
    
        function expandir(barra){
           eval("document."+barra+".src = "+barra+"ON.gif");
           eval(barra+"DIV.visibility = VISIBLE;");
        }
    
        function contraer(barra){
           eval("document."+barra+".src = "+barra+"OFF.gif");
           eval(barra+"DIV.visibility = INVISIBLE;");
        }
    </SCRIPT>
          
    Como vemos, las tres versiones de código JS utilizan los mismos nombres de funciones. De esta forma conseguimos que las distintas funciones tengan distinta funcionalidad (más cuanto más avanzada es la versión de JS).

    Es conveniente situar código JavaScript delante de JavaScript1.1, y éste delante de JavaScript1.2, porque un navegador usará sólo la última definición de función que encuentre cuando esté ejecutando el script. Así, cada navegador ejecutará una función u otra, dependiendo de lo antiguo que sea, ante un evento o llamada desde otra función.

  5. Crear múltiples sitios:

    Esta es la solución extrema. Sólo deberemos aceptarla en el caso de que nada de lo visto hasta ahora nos sirva, esto es, no podamos hacer compatibles las páginas para ser vistas en todos los navegadores.

    Otra razón por la que puede que queramos diferentes conjuntos de páginas para diferentes navegadores es porque, si queremos hacer unas páginas DHTML "espectaculares", intentar hacerlas compatibles con otros navegadores (especialmente los antiguos) nos puede restringir mucho a la hora de conseguir ciertos efectos, animaciones u ordenaciones del contenido.

    Pues bien, si decidimos hacer sitios separados, para HTML básico y DHTML, hay algunas cosas que deberíamos hacer para que el usuario final no lo note demasiado.

    Redirigir con META-REFRESH: Es la técnica utilizada para redirigir a otra página cuando la localización ha cambiado.

    Utilizando esta técnica, junto con alguna vista anteriormente, podemos hacer páginas que dirijan al usuario a las páginas que sí soporte su navegador. Un ejemplo de esto es:

    <HEAD>
    <SCRIPT LANGUAGE="JavaScript1.2">
        //cargar la pagina para el navegador moderno
        location = "index4.html" ;
    </SCRIPT>
    
    <META HTTP-EQUIV = refresh CONTENT="0; URL=index3.html">
    </HEAD>
    
    <BODY>
    </BODY>
          
    Esta técnica se suele utilizar como página principal de un sitio, de forma que en ella sólo se pone un aviso de espera, mientras que lo que se hace, de forma transparente al usuario, es redirigir a otra página.

    Redirigir dinámicamente: A veces no hay que hacer sitios completamente separados, sino que sólo hay que tener algunas páginas separadas para los distintos navegadores , en las cuales se utilizan las técnicas vistas aquí para hacerlas compatibles con todos los navegadores, a la vez que hay ciertas páginas especialmente diseñadas para E4 y N4 y paralelamente otras para E3 y N3,

    En este caso, hacemos la redirección en el mismo link, del siguiente modo:

    <A HREF="pagina3.html" 
     onClick="return dirigelink('pagina4.html');">
        Ir a la siguiente página
    </A>
    
    <SCRIPT>
       function dirigelink( pagina ) {
           if( navigator.appVersion.charAt(0) == '4' ){
               location.href = pagina ;
               return false;
           }
           return false;
       }
    </SCRIPT>
          
    La ventaja que tiene la Redirección dinámica es que por cada página sólo se hace una petición al servidor, mientras que con META-REFRESH se hacen dos (una para la página principal que decide a qué segunda página te manda).






5.8.- INCLUIR FICHEROS HTML EN LAYERS.

Acerca de este punto hemos hablado en las propiedades del tag <LAYER>. Sin embargo, intentaremos ampliarla explicando ciertos puntos que puedan haber quedado "sueltos".

La propiedad include-source de CSS es válida sólo en N4, con lo cual E4 la ignora. Sin embargo, E4 también nos permite la inclusión de documentos HTML en layers, mediante el tag (que sólo E4 reconoce) <IFRAME>, del siguiente modo:

<IFRAME SRC="inc1.htm" NAME="textframe" SCROLLING="No" 
 WIDTH="300" HEIGHT="200" MARGINWIDTH=0 MARGINHEIGHT=0 
 FRAMEBORDER="No">
</IFRAME>
Los atributos del tag (y su significado) son:
    • SRC: especifica el fichero a incluir.
    • NAME: indica el nombre dado al elemento, como hasta ahora con el resto de elementos.
    • SCROLLING: indica si permitimos scroll en el elemento ("Yes" o "No").
    • WIDTH: indica la anchura del elemento.
    • HEIGHT: indica la altura del elemento.
    • MARGINWIDTH: indica la anchura del margen del elemento.
    • MARGINHEIGHT: indica la altura del margen del elemento.
    • FRAMEBORDER: indica si deseamos ponerle borde al elemento ("Yes" o "No").
Una diferencia entre usar un layer junto con include-source o <IFRAME> es que el último es totalmente opaco, con lo que actúa como si el layer correspondiente tuviese un color de fondo (no null). Sin embargo, podemos poner un layer trasparente sobre el <IFRAME> y éste se verá a través de las zonas trasparentes.

Otra diferencia entre navegadores es que puesto que E4 lo que implementa realmente es un FRAME, podemos hacer que tenga scroll, mientras que en N4, no podemos disponer de esto, a no ser que hagamos nosotros mismos el scroll mediante JS.

Otro problema que se plantea con la inclusión de ficheros HTML es que éstos pueden incluir código JS, con lo cual el modo de acceder a las funciones JS que se incluyen en ese fichero cambia del N4 al E4. Mientras que con N4 debemos hacer referencia al layer en el que está contenido el código para llamar a la función:

document.layerID.nombreFuncion( parametros );
      
en E4, podemos hacer la llamada directamente, del siguiente modo:
nombreFuncion( parametros );
      

Ver ejemplo de inclusión de documentos en layers (solo N4): Source-include.

Ver ejemplo de inclusión de documentos en layers (IE4 y N4): Source-include y IFRAMES.

 
Pedro Angel Castillo Valdivieso: pedro@geneura.ugr.es
Fco. Javier García Castellano: fjgc@decsai.ugr.es
Equipo GeNeura
Departamento de Arquitectura y Tecnología de los Computadores
Universidad de Granada
tlf: +34-58-243163
fax: +34-58-243230