Contenedores con pestañas

Un contenedor con pestañas o tabs es una forma de incluir mucha información en una misma superficie. Es también un buen ejercicio para practicar CSS y Javascript. En Internet hay multitud de tutoriales sobre este tema. Muchos ejemplos son realmente atractivos.

En la parte de cómo se hace un formulario emergente con pestañas contemplé la posibilidad de incorporar contenedores de pestañas a una ventana emergente. La apariencia de una parte del emergente es como esta captura con el navegador Internet Explorer 8:

bordes correctos en ie

Estos contenedores de pestañas se estructuran en una tabla (elemento <table>) para aprovechar la capacidad de reformateo dinámico que tienen las tablas. Se trata de una tabla con dos filas. En la primera fila ponemos el doble de celdas que pestañas necesitemos, alternando una celda con un espacio no separable &nbsp; y otra con el título de la pestaña correspondiente. La segunda fila sólo tiene una celda pero con abarcamiento colspan="2N", es decir, abarcando las 2N pestañas siendo N el número de pestañas que necesitamos. Dentro de esa celda disponemos los N contenedores <div> donde el primero tiene la propiedad {display: block} y los otros con el valor none. De esta forma y con Javascript cuando hagamos click en una pestaña la pondremos a block y el resto a none. Esta nos dará la apariencia de que esa pestaña y su contenedor se viene al frente.

Aunque más adelante entraremos en el detalle de todo esto, por ahora vale decir que esa no es la única forma de hacerlo, pues usando estilo de capas con {z-index} también es posible y habrán otras tantas que desconozco. Un ejemplo más bien simple que realicé en mis inicios con HTML/CSS aplicando esto puede verlo en menú con pestañas, que aunque es para combinar diferentes documentos, también podría aplicarse a diferentes contenedores encerrados dentro de otro contenedor. Pero esto resulta más complejo que las tablas, pues hay que controlar los aspectos de posicionamiento y, entre otras cosas, la estructura se vería deformada si no se controlan los contenidos y los anchos disponibles. Esto sin embargo no sucede gracias al reformateo dinámico de las tablas, aspecto de lo que se encarga el navegador.

El interior y por tanto las pestañas de esos formularios emergentes son estructuras HTML generadas dinámicamente. En los temas sobre ventanas emergentes se expone el código Javascript para crearlas, en el método this.creaTabs. Pero a veces necesitamos crear contenedores con pestañas directamente en nuestro HTML sin que sea generado con Javascript (aunque éste podría ser necesario para gestionarlas). De hecho tengo creadas otras diferentes que ideé para usar en una herramienta que se describe en cómo construir índices de buscadores y que reproduzco parcialmente a continuación.

herramienta con pestañas

Aunque son visualmente menos atractivas, nos permiten estructurar un volumen grande de controles de formualrio al contar con un tbody, que a su vez puede incluir indefinidos <tr>, para cada contenedor de pestaña.

Requisitos de diseño para los contenedores de pestañas

La estructura básica debe ser fácil de modificar, de tal forma que sea independiente del número de pestañas. Así si están basadas en CSS y/o Javascript tendría que ser suficiente con añadir un nuevo elemento o grupo de elementos HTML para insertar un nueva pestaña, sin que tenga que ser necesario modificar el CSS y/o Javascript.

Esta independencia del número de pestañas puede ser una ventaja si construimos el contenedor desde un proceso dinámico de Javascript. Sólo bastaría con generar los elementos HTML sin necesidad de tocar el CSS o Javascript que lo maneja. Así hice la estructura de pestañas que incluyo en mi formulario o ventana emergente.

Para mostrar y ocultar los distintos contentedores hemos de usar necesariamente algún mecanismo que reciba el evento click sobre el título de la pestaña. Esto es fácil con Javascript usando el evento onclick en ese elemento. En el tema siguiente veremos esos dos ejemplos anteriores. Sin embargo si sólo queremos usar CSS sin Javascript la cosa se complica. Una forma de recoger el evento sobre un elemento sin Javascript es usar las selectores CSS de pseudoclases. El selector que parece más apropiado es E:focus, pero como veremos en el último tema, hay más de un problema que resolver.

Bordes superpuestos de las celdas de una tabla

En el primer apartado anterior puse una imagen de como se presenta una parte del formulario emergente con pestañas en Explorer. Aunque en éste y otros navegadores con los que hice pruebas (Firefox y Safari) se mostraban correctamente, si embargo tuve problemas con el navegador Opera que incluso actualmente seguía sin mostrar adecuadamente los bordes de las pestañas y del contenedor, como se observa en esta captura de pantalla de Opera 11.50 hecha en el momento en que estoy redactando este texto (Julio 2011) y antes de aplicarle la corrección de este error de concepto:

problema bordes opera

Con la carga de la página sólo aparecían los fondos grises de las pestañas pero faltaba el borde del contenedor y de la primera pestaña. Si se pulsaba sobre la segunda, Opera parecía hacer un intento de pintado, pero aún no se mostraba correctamente el borde del contenedor:

problema bordes opera

En ese tema expuse el comentario "Opera 10.6 no actualiza adecuadamente el borde de las pestañas. Aún no he descubierto la raíz del problema, pues Internet Explorer 8, Firefox 3.6 y Safari 4.0 si lo hacen". Pues bien, el problema hasta el momento seguía siendo un misterio para mí. Y antes de realizar los ejemplos que expongo en el capítulo siguiente basados en estructuras de tablas, decidí volver a repasar los conceptos relacionados con los modelos de bordes de las tablas HTML para intentar solucionar esto.

El modelo de bordes de las tablas

Los bordes de una tabla es un tema bastante complejo debido a que HTML-4.01 puede modificar su apariencia como atributos y por otro lado CSS-2.1 también tienen propiedades de estilo que actuan sobre ellos. Es lo que se denomina como el modelo de bordes abiertos y cerrados de la tabla. En ese apartado entré a analizar como se comportan esos modelos, donde puede verse que por defecto una tabla tiene la propiedad border-collapse con el valor separate. Este es el modelo de bordes abiertos que da lugar a que se separen las celdas unos 2px por defecto, como se aprecia en esta tabla de ejemplo cuyo código es:

<table>
    <tr>
        <td style="border: blue solid 10px">&nbsp;&nbsp;&nbsp;</td>
        <td style="border: red solid 10px">&nbsp;&nbsp;&nbsp;</td>
    </tr>
    <tr>
        <td style="border: green solid 10px">&nbsp;&nbsp;&nbsp;</td>
        <td style="border: black solid 10px">&nbsp;&nbsp;&nbsp;</td>
    </tr>
</table>
      
      

Se observa que la tabla no tiene ningún atributo y que el estilo incorpora bordes a cada celda con diferentes colores. La separación de 2px entre las celdas es dada automáticamente por el navegador. Esto se presenta igual en todos los navegadores. Si queremos evitar estas separaciones podemos usar el modelo de bordes cerrados con el estilo {border-collapse: collapse} para la tabla anterior:

<table style="border-collapse: collapse">
    ... 
      
      

De hecho esta es la solución que usé para el tema de las pestañas en el formulario emergente, pues había que suprimir el espacio entre la celda de contenido en la fila inferior y las celdas de las pestañas en la fila superior. Entonces ¿porqué Opera no pinta adecuadamente los bordes?. Para averiguarlo probé la tabla anterior con los navegadores actualizados Explorer 8.0 (IE), Google Chrome 12.0 (CH), Firefox 5.0 (FF), Safari 5.0 (SA) y Opera 11.5 (OP). Las capturas de pantalla de la tabla anterior son:

IECHFFSAOP
ieieieieie
Capturas de pantalla de la representación del modelo de bordes cerrados.

Hasta cierto punto me sorprendió la gran diferencia de presentaciones, aunque si lo piensa uno bien se da cuenta que el manejo de tablas es uno de los aspectos de HTML/CSS donde se contradicen los navegadores. Como el modelo de bordes cerrados hace que las celdas compartan los bordes contiguos (los bordes se superponen), entonces dependiendo de como el navegador pinte esos bordes tendremos distintos comportamientos. Y eso es lo que ocasionaba mi problema. Podemos verlo asi: Explorer comienza pintando desde la última celda inferior derecha y acaba en la superior izquierda, de tal forma que "machaca" el color de la celda anteriormente pintada. Chrome hace algo parecido, pero no sé como vuelve a "repintar" la inferior derecha. Firefox y Safari hacen lo mismo pero realmente tampoco soy capaz de decir lo que hacen. Opera en cambio empieza por la primera celda superior izquierda y acaba en la última inferior derecha.

En definitiva, mi problema surge al usar el modelo de bordes cerrados con {border-collapse: collapse} tal que todos los navegadores pintan el borde inferior de las celdas superiores a excepción de Opera, que los repinta con el borde superior de las celdas inferiores. Para evitar esto cuando hay un manejo en las características de los bordes adyacentes, hay que usar el modelo de bordes abiertos, el que viene por defecto en las tablas pero que podemos incluir en el estilo con {border-collapse: separate} para no olvidarnos. Y además declarar la propiedad {border-spacing: 0} que hace que no haya separación entre celdas:

<table style="border-collapse: separate; border-spacing: 0">
    ... 
      
      

Ahora los bordes de las celdas son adyacentes y no se superponen. El único problema es que el grosor de los bordes internos se duplica. Pero para nuestro ejemplo no es importante pues los bordes adyacentes entre las celdas que hacen de pestañas se ocultan o se muestran pero nunca aparecen dos adyacentes de diferente color. Por lo tanto no es que el modelo de bordes cerrados no sea de aplicación en general, es más, deberá aplicarse cuando queramos un único y uniforme borde interno de un sólo grosor y color entre las celdas adyacentes como este ejemplo:

<table style="border-collapse: collapse">
    <tr>
        <td style="border: blue solid 1px">Una tabla</td>
        <td style="border: blue solid 1px">con bordes sencillos</td>
    </tr>
    <tr>
        <td style="border: blue solid 1px">en todas</td>
        <td style="border: blue solid 1px">sus celdas</td>
    </tr>
</table>
Una tablacon bordes cerrados
en todassus celdas

Si la hiciéramos con el modelo de bordes abiertos con el estilo border-collapse: separate y border-spacing: 0

Una tablacon bordes abiertos
en todassus celdas

vemos que el grosor de los bordes internos se duplica.