Outbook

HTML+CSS+JS, Accesibilidad, PHP y más

Datos de búsqueda

Categoría: ‘XHTML’

PHP: función para crear calendario accesible y semántico

Fecha de publicación: 2008/12/02

He aquí una función en PHP para generar un calendario con XHTML semántico y accesible, y con clases e identificadores adecuados para aplicarle rápidamente el CSS.

La función lleva cuatro parámetros, todos ellos opcionales, en el siguiente orden:

  • Año ($year): Define el año que se va a mostrar, siendo un valor de 4 dígitos. Se predetermina al año actual.
  • Mes ($mes): Define el mes a mostrar, con valores de 1 a 12. Se predetermina al mes actual.
  • Estado fines de semana ($finDeSemana=1): Define si los días de los fines de semana llevan o no enlace. Se le da valor 1 para mostrarlos con enlace, o valor 0 para quitar el enlace. Se predetermina a 1.
  • Estado de días nulos ($mostrarDiasNulos=1): Define si se muestran los días de la primera semana que pertenecen al mes anterior y los días de la última semana que pertenecen al mes posterior. Se le da valor 1 para mostrarlos, o valor 0 para ocultarlos. Se predetermina a 1.
  • Nivel de encabezado ($nivelH=2): Nivel del encabezado del bloque de calendario. Ha de terner un valor de entre 1 y 6. Se predetermina en 2.

Ejemplo de llamada a la función con el mes de febrero de 2009, los fines de semana desactivados, los días nulos activados y un encabezado de nivel 3:

calendario(2009,2,0,1,3);

Descargar archivo con ejemplo funcional o verlo en acción (abre en ventana nueva).

Funciones para generar el calendario (para mayor seguridad, utilizar el código del ejemplo descargable):

function calendario ($year,$mes,$finDeSemana=1,$mostrarDiasNulos=1,$nivelH=2) {
 
 if (strlen($year)!=4) {$year=date('Y');}
 if (($mes<1 or $mes>12) or (strlen($mes)<1 or strlen($mes)>2)) {$year=date('n');}
 
 // Listados: días de la semana, letra inicial de los días de la semana, y meses
 $dias = array('Lunes','Martes','Miércoles','Jueves','Viernes','Sábado','Domingo');
 $diasAbbr = array('L','M','M','J','V','S','D');
 $meses = array('Enero','Febrero','Marzo','Abril','Mayo','Junio','Julio','Agosto','Septiempre','Octubre','Noviembre','Diciembre');
 
 // Se sacan valores que se utilizarán más adelante
 $diaInicial = gmmktime(0,0,0,$mes,1,$year);  // Primer día del mes dado
 $diasNulos = (date("N",$diaInicial))-1; // Con 'N' la semana empieza en Lunes. Con 'w', en domingo
  if($diasNulos<0){$diasNulos = 7-abs($diasNulos);}
 $diasEnMes = date("t",$diaInicial); // Número de días del mes dado
 
 // Se abre la capa contenedora y se genera el encabezado del bloque de calendario
 $html .= '<div id="calendario">';
 $html .= '<h'.$nivelH.' class="encabezadoCalendario">Calendario</h'.$nivelH.'>';
 
 // Párrafos con la fecha actual y la fecha seleccionada
 $html .= '<p>Fecha actual: '.date('j').' de '.$meses[(intval(date('n'))-1)].' de '.date('Y').'</p>';
 $html .= '<p>Fecha seleccionada: ';
 if (isset($_GET['dia'])) {$html .= ''.$_GET['dia'].' de ';} // El día solo sale si se ha definido previamente en el parámetro 'dia' de la URL
 $html .= ''.$meses[($mes-1)].' de '.$year.'</p>';
 $html .= '<div class="tabla">';
 
 
 // Enlaces al mes anterior y al siguiente
 $html .= '<p>Navegación por meses:</p>';
 $html .= '<ul id="calNavMeses">';
 $enlaceAnterior1 = gmmktime(0,0,0,($mes-1),1,$year);
 $mesAnterior = date('n',$enlaceAnterior1);
 $yearMesAnterior = date('Y',$enlaceAnterior1);
 $enlaceSiguiente1 = gmmktime(0,0,0,($mes+1),1,$year);
 $mesSiguiente = date('n',$enlaceSiguiente1);
 $yearMesSiguiente = date('Y',$enlaceSiguiente1);
 $html .= '<li class="anterior"><a href="?mes='.$mesAnterior.'&ano='.$yearMesAnterior.'"><span>Mes anterior ('.$meses[($mesAnterior-1)].')</span></a></li>';
 $html .= '<li class="siguiente"><a href="?mes='.$mesSiguiente.'&ano='.$yearMesSiguiente.'"><span>Mes siguiente ('.$meses[($mesSiguiente-1)].')</span></a></li>';
 $html .= '</ul>';
 
 // Enlaces al año anterior y al siguiente
 $html .= '<p>Navegación por años:</p>';
 $html .= '<ul id="calNavYears">';
 $enlaceAnterior2 = gmmktime(0,0,0,$mes,1,($year-1));
 $yearAnterior = date('Y',$enlaceAnterior2);
 $enlaceSiguiente2 = gmmktime(0,0,0,$mes,1,($year+1));
 $yearSiguiente = date('Y',$enlaceSiguiente2);
 $html .= '<li class="anterior"><a href="?mes='.$mes.'&ano='.$yearAnterior.'"><span>Año anterior (</span>'.$yearAnterior.'<span>)</span></a></li>';
 $html .= '<li class="siguiente"><a href="?mes='.$mes.'&ano='.$yearSiguiente.'"><span>Año siguiente (</span>'.$yearSiguiente.'<span>)</span></a></li>';
 $html .= '</ul>';
 
 // Se abre la tabla que contiene el calendario
 $html .= '<table>';
 
 // Título mes-año (elemento CAPTION)
 $mesLista = $mes-1;
 $html .= '<caption>'.$meses[$mesLista].'<span> de</span> '.$year.'</caption>';
 
 // Se definen anchuras en elementos COL
 $cl=0; $anchoCol=100/7; while ($cl<7) {$html .= '<col width="'.$anchoCol.'%" />'; $cl++;}
 
 // Fila de los días de la semana (elemento THEAD)
 $html .= '<thead><tr>';$d=0;
 while ($d<7) {$html .= '<th scope="col" abbr="'.$dias[$d].'">'.$diasAbbr[$d].'</th>';$d++;}
 $html .= '</tr></thead>';
 
 // Se generan los días nulos (días del mes anterior o posterior) iniciales, el TBODY y su primer TR
 $html .= '<tbody>';
 if ($diasNulos>0) {$html .= '<tr>';} // Se abre el TR solo si hay días nulos
 if ($diasNulos>0 and $mostrarDiasNulos==0) {$html .= '<td class="nulo" colspan="'.$diasNulos.'"></td>';} // Se hace un TD en blanco con el ancho según los día nulos que haya
 if ($mostrarDiasNulos==1) { // Generación de los TD con días nulos si está activado que se muestren
  $dni=$diasNulos;$i=0;
  while ($i<$diasNulos) {
   $enSegundosNulo = gmmktime(0,0,0,$mes,(1-$dni),$year);
   $dmNulo = date('j',$enSegundosNulo);
   $idFechaNulo = 'cal-'.date('Y-m-d',$enSegundosNulo);
   $html .= '<td id="'.$idFechaNulo.'" class="diaNulo"><span class="dia"><span class="enlace">'.$dmNulo.'</span></span></td>';
   $dni--;
   $i++;
  }
 }
 
 
 
 // Se generan los TD con los días del mes
 $dm=1;$x=0;$ds=$diasNulos+1;
 while ($dm<=$diasEnMes) {
  if(($x+$diasNulos)%7==0 and $x!=0) {$html .= '</tr>';} // Se evita el cierre del TR si no hay días nulos iniciales
  if(($x+$diasNulos)%7==0) {$html .= '<tr>';$ds=1;}
  $enSegundosCalendario = gmmktime(0,0,0,$mes,$dm,$year); // Fecha del día generado en segundos
  $enSegundosActual = gmmktime(0,0,0,date('n'),date('j'),date('Y')); // Fecha actual en segundos
  $enSegundosSeleccionada = gmmktime(0,0,0,$_GET['mes'],$_GET['dia'],$_GET['ano']); // Fecha seleccionada, en segundos
  $idFecha = 'cal-'.date('Y-m-d',$enSegundosCalendario);
  
  // Se generan los parámetros de la URL para el enlace del día
  $link_dia = date('j',$enSegundosCalendario);
  $link_mes = date('n',$enSegundosCalendario);
  $link_year = date('Y',$enSegundosCalendario);
  
  // Clases y etiquetado general para los días, para día actual y para día seleccionado
  $claseActual='';$tagDia='span';
  if ($enSegundosCalendario==$enSegundosActual) {$claseActual=' fechaHoy';$tagDia='strong';}
  if ($enSegundosCalendario==$enSegundosSeleccionada and isset($_GET['dia'])) {$claseActual=' fechaSeleccionada';$tagDia='em';}
  if ($enSegundosCalendario==$enSegundosActual and $enSegundosCalendario==$enSegundosSeleccionada and isset($_GET['dia'])) {$claseActual=' fechaHoy fechaSeleccionada';$tagDia='strong';}
  
  // Desactivación de los días del fin de semana
  if (($ds<6 and $finDeSemana==0) or $finDeSemana!=0) { // Si el fin de semana está activado, o el día es de lunes a viernes
   $tagEnlace='a';
   $atribEnlace='href="?dia='.$link_dia.'&mes='.$link_mes.'&ano='.$link_year.'"';
  } if ($ds>5 and $finDeSemana==0) { // Si el fin de semana está desactivado y el día es sábado o domingo
   $tagEnlace='span';
   $atribEnlace='';
   $paramFinde='0';
  }
  
  // Con las variables ya definidas, se crea el HTML del TD
  $html .= '<td id="'.$idFecha.'" class="'.calendarioClaseDia($ds).$claseActual.'"><'.$tagDia.' class="dia"><'.$tagEnlace.' class="enlace" '.$atribEnlace.'>'.$dm.'</'.$tagEnlace.'></'.$tagDia.'></td>';
  
  $dm++;$x++;$ds++;
 }
 
 // Se generan los días nulos finales
 $diasNulosFinales = 0;
 while((($diasEnMes+$diasNulos)%7)!=0){$diasEnMes++;$diasNulosFinales++;}
 if ($diasNulosFinales>0 and $mostrarDiasNulos==0) {$html .= '<td class="nulo" colspan="'.$diasNulosFinales.'"></td>';} // Se hace un TD en blanco con el ancho según los día nulos que haya (si no se activa mostrar los días nulos)
 if ($mostrarDiasNulos==1) { // Generación de días nulos (si se activa mostrar los días nulos)
  $dnf=0;
  while ($dnf<$diasNulosFinales) {
   $enSegundosNulo = gmmktime(0,0,0,($mes+1),($dnf+1),$year);
   $dmNulo = date('j',$enSegundosNulo);
   $idFechaNulo = 'cal-'.date('Y-m-d',$enSegundosNulo);
   $html .= '<td id="'.$idFechaNulo.'" class="diaNulo"><span class="dia"><span class="enlace">'.$dmNulo.'</span></span></td>';
   $dnf++;
  }
 }
 
 // Se cierra el último TR y el TBODY
 $html .= '</tr></tbody>';
 
 // Se cierra la tabla
 $html .= '</table>';
 
 // Se cierran la capa de la tabla y la capa contenedora
 $html .= '</div>';
 $html .= '</div>';
 
 // Se devuelve la variable que contiene el HTML del calendario
 return $html;
}

function calendarioClaseDia ($dia) {
 switch ($dia) {
  case 1: $clase = 'lunes semana'; break;
  case 2: $clase = 'martes semana'; break;
  case 3: $clase = 'miercoles semana'; break;
  case 4: $clase = 'jueves semana'; break;
  case 5: $clase = 'viernes semana'; break;
  case 6: $clase = 'sabado finDeSemana'; break;
  case 7: $clase = 'domingo finDeSemana'; break;
 }
 return $clase;
}

Actualizaciones

Actualizado 2008-12-03: Se añade la posibilidad de desactivar los fines de semana.

Actualizado 2008-12-04: Se añade la posibilidad de desactivar o activar los días de la primera semana que son del mes anterior y los de la última que son del mes siguiente (denominados como días nulos). Se explican los parámetros a introducir al llamar a la función.

Actualizado 2008-12-05: Mejoras de accesibilidad.

Actualizado 2009-02-01: Corregido el fragmento de código expuesto en la página.

Licencia

A diferencia de otros contenidos la licencia para este artículo y el ejemplo adjunto es Reconocimiento-Compartir bajo la misma licencia 3.0 España.

Datos del artículo:

Enlaces como bloque en Internet Explorer 6

Fecha de publicación: 2007/10/03

El problema

Tengo unos enlaces dentro de elementos de lista, para un menú de navegación.

Esos enlaces se convierten en bloque mediante CSS, con la propiedad display:block; .

En Firefox, Opera e Internet Explorer 7 el enlace funciona correctamente, aunque no se pase el puntero por encima del texto de enlace.

En Internet Explorer 6 hay que pasar el puntero por encima del texto de enlace, en caso contrario el enlace no se activa.

ul {list-style-type: none; margin:0; padding:0; width:160px;}
ul li {background:#FFFF99; margin-bottom:3px; border-bottom:1px solid #fff;}
ul li a {display:block; padding:4px;}
ul li a:hover {background-color:#FFCC66;}

La solución

Darle una anchura fija al enlace:

ul {list-style-type: none; margin:0; padding:0; width:160px;}
ul li {background:#FFFF99; margin-bottom:3px; border-bottom:1px solid #fff;}
ul li a {display:block; padding:4px;width:160px;width:152px;}
ul li a:hover {background-color:#FFCC66;}

Ejemplo de funcionamiento

Atención: el efecto solo se observará en Internet Explorer 6 y anteriores.

Enlace sin anchura definida

Enlace con anchura definida

Datos del artículo:

CSS sprites (2): posicionamiento de la imagen de fondo

Fecha de publicación: 2007/09/18

Este artículo es una ampliación a CSS Sprites: rollover utilizando una sola imagen.

En el artículo anterior se daban valores negativos para el eje de coordenadas Y en la propiedad CSS background-position. En este caso el valor se puede obtener de la siguiente forma: se resta a la altura total de la imagen la altura del sprite que se quiere ver y la coordenada Y de la propiedad background-position.

Ejemplo: la altura total de una imagen con 2 sprites es de 100px. Cada sprite tiene una altura de 6px, y el sprite que está inicialmente tiene un background-position:0 4px;. Entonces se hará la siguiente resta: 100px-6px-4px=90. La posición del eje Y que habrá que dar será -90px.

Datos del artículo:

HTML: el atributo scope

Fecha de publicación: 2007/07/31

El atributo scope se utiliza para vincular la información contenida en las diferentes celdas de una tabla.

Este atributo especifica el conjunto de celdas cuya información se refiere a la celda que contiene este atributo.

Supone una alternativa simple al atributo headers.

El atributo scope puede tomar uno (solamente uno) de los siguientes valores:

  • row. La celda se refiere al resto de celdas que componen su fila.
  • col. La celda se refiere al resto de celdas que componen su columna.
  • rowgroup. La celda se refiere al las celdas que componen su grupo de filas.
  • colgroup. La celda se refiere al las celdas que componen su grupo de columnas.

Saber más sobre el atributo scope.

Datos del artículo:

Las tres capas del diseño web

Fecha de publicación: 2007/06/26

Las interfaces web se componen de tres capas:

  • Contenido.
  • Presentación.
  • Comportamiento.

Presentación y comportamiento son opcionales.

Contenido

Se trata del contenido y de su estructuración semántica: el documento HTML. Cada porción de texto deberá ser contenida en el elemento adecuado a su contexto (párrafos, listas, encabezados, etc.), o llevar la información adicional que sea necesaria (el texto alternativo en las imágenes, por ejemplo).

Aquí no se definirá absolutamente nada relacionado con la presentación: nada de configurar colores, tipografías, fondos, etc. Nada.

Si todo esto se hace correctamente se habrá dado un paso muy importante hacia la accesibilidad, y también en cuanto al posicionamiento en buscadores. El documento será legible sin necesidad de las otras dos capas.

Presentación

En esta capa se definirá el aspecto del contenido, mediante hojas de estilo CSS.

El CSS se introduce preferentemente en un archivo, o varios, separados del contenido.

Se pueden aplicar hojas de estilo para dispositivos específicos: pantalla (la más habitual), PDA, impresoras, lectores de pantalla, etc. De esta forma se podrá optimizar el aspecto de un sitio web atendiendo a las necesidades específicas de cada dispositivo.

Además hacer la capa de presentación separada de la de contenido tiene otra ventaja importante: con solo cambiar la hoja de estilo, cambia el aspecto de todo el sitio al que se aplique la misma. Un ejemplo interesante de esta ventaja es el CSS Zen Garden, donde con un mismo contenido pero con diferentes hojas de estilo se consiguen resultados muy variados.

Comportamiento

Se trata de añadir funcionalidades con algo más de complejidad, como elementos desplegables, rollovers, etc. Se hace mediante Javascript que interactue con el DOM.

El código Javascript deberá estar contenido en archivos externos, igual que las hojas de estilo en la capa anterior.

La adición de esta capa no debe impedir el correcto acceso al contenido.

Saber más

Datos del artículo:

Internet Explorer 6: Duplicación de caracteres

Fecha de publicación: 2007/06/07

Actualizado 2010-06-02: solución del color de fondo.

Hay ocasiones en las que Internet Explorer 6 duplica caracteres. Se trata de un molesto bug que se da, en principio, ante las siguientes condiciones:

  • Que haya dos o más comentarios seguidos.
  • Que el elemento flotado quede a menos de 3px del límite derecho de su contenedor.

Con un solo comentario no ocurre nada, pero con dos o más comentarios seguidos se manifiesta este molesto bug. A mayor cantidad de comentarios, mayor cantidad de caracteres repetidos.

Ejemplo de código para que aparezca el problema descrito:

<div style="float:left">De esta no se repiten</div>

<!-- Ambos comentarios deben incluirse para que salga el fallo -->
<!-- Ambos comentarios deben incluirse para que salga el fallo -->

<div style="float:left; width:100%">
En esta se van a repetir caracteres.
</div>

Y sale algo como esto en IE6 (la última línea es lo que se repite):

repeated characters

s.

Hay dos soluciones:

  • Quitar los comentarios.
  • Meter comentarios condicionales, en lugar de los habituales, que sean ignorados por Internet Explorer, y que el resto de navegadores tratarán como si se tratase de un comentario normal.

Otra posible solución será asignarle al elemento un color de fondo en la CSS.

Un ejemplo del comentario condicional:

<!--[if !IE]>Inserta aquí el comentario<![endif]-->

Saber más

Datos del artículo:

Javascript: ejecutar una función en la carga de página

Fecha de publicación: 2007/06/05

Existen al menos dos formas:

  • Mediante el atributo onload en el elemento body. El valor de dicho atributo será la función Javascript que se quiera ejecutar.
  • En el archivo Javascript que se vincule desde la página, mediante window.onload, y mencionando a continuación la función que se desea ejecutar.

Un ejemplo del uso del onload:

<body onload="funcion_a_ejecutar(parametro)">[...]</body>

Un ejemplo para hacerlo desde un archivo Javascript:

window.onload = funcion_a_ejecutar;

Lo adecuado sería usar la segunda opción, ya que ayuda a mantener limpio el código HTML.

Saber más

Datos del artículo:

HTML-CSS: Listas multicolumna (1)

Fecha de publicación: 2007/05/30

Existe un método muy sencillo para crear una lista que se muestre en varias columnas, con el inconveniente de que no siempre funciona bien en Internet Explorer, como en este caso, pero creo que la idea es interesante.

En los elementos de lista (LI) se insertará una clase (class="colx", donde x es el número de columna) que indica en que columna se sitúa, y en el primer elemento de la columna otra clase que indique el comienzo de la columna (en este caso son 17 elementos de lista en 3 columnas):

<ul>
 <li class="col1">Elemento de lista 12</li>
 <li class="col1">Elemento de lista 2</li>
 <li class="col1">Elemento de lista 3</li>
 <li class="col1">Elemento de lista 4</li>
 <li class="col1">Elemento de lista 5</li>
 <li class="col1">Elemento de lista 6</li>
 <li class="col2 sube">Elemento de lista 7</li>
 <li class="col2">Elemento de lista 8</li>
 <li class="col2">Elemento de lista 9</li>
 <li class="col2">Elemento de lista 10</li>
 <li class="col2">Elemento de lista 11</li>
 <li class="col2">Elemento de lista 12</li>
 <li class="col3 sube">Elemento de lista 13</li>
 <li class="col3">Elemento de lista 14</li>
 <li class="col3">Elemento de lista 15</li>
 <li class="col3">Elemento de lista 16</li>
 <li class="col3">Elemento de lista 17</li>
</ul>

Y ahora el CSS. Se le puede dar una anchura al elemento UL, en este caso 630px. Ahora ese ancho hay que repartirlo entre las columnas, a las que se le dará 200px, después de quitarles 5px en cada lado para separarlas unas de otras. Ahora hay que dar altura, 16px, con un line-height. A cada columna, definida por las clases de los elementos LI, se le define un margen izquierdo, estrechamente relacionado con las anchuras de los UL y LI. El primer LI de cada columna lleva una clase a la que se le dará un margin-topnegativo equivalente a la altura de cada columna, para que todas ellas queden a la misma altura: 96px (16px por los 6 elementos de lista LI en cada columna).

Código CSS

ul {width:630px;}
ul li {width:200px;margin:0 5px;line-height:16px;}
ul li.col1 {margin-left:0;}
ul li.col2 {margin-left:210px;}
ul li.col3 {margin-left:420px;}
ul li.sube {margin-top:-96px;}

Ver ejemplo

La limitación de este método es que se ha definido una altura de línea en los elementos de lista (LI), lo cual hace que la clase sube pierda su eefectividad en caso de que uno de dichos elementos de lista tenga más de una línea.

Referencia y enlaces relacionados

Datos del artículo:

HTML: abreviaturas en los encabezados de tabla

Fecha de publicación: 2007/05/16

Cuando el contenido de un encabezado de tabla (TH) es muy extenso se puede recurrir a su abreviatura, mediante el atributo abbr.

Esta técnica tiene sentido para los usuarios que utilizan lectores de pantalla, ya que dichos lectores repiten el contenido de los encabezados de tabla al referirse a una celda asociado a los mismos.

Ejemplo de tabla accesible con abreviaturas en los encabezados de tabla (la tabla la he tomado prestada de 456 Berea St.):

<table summary="Número de empleados y años de fundación de
 empresas ficticias.">
 <caption>Tabla 1: Datos de las empresas</caption>
 <tr>
  <th abbr="Empresa">Nombre de la empresa</th>
  <th abbr="Empleados">Número de empleados</th>
  <th abbr="Fundación">Año de fundación</th>
 </tr>
 <tr>
  <td>ACME Inc</td>
  <td>1000</td>
  <td>1947</td>
 </tr>
 <tr>
  <td>Compañía XYZ</td>
  <td>2000</td>
  <td>1973</td>
 </tr>
</table>

Referencias y enlaces relacionados

Datos del artículo:

CSS: centrado vertical

Fecha de publicación: 2007/05/07

Supongamos que tenemos un párrafo (elemento P) dentro de una capa (elemento DIV) y quueremos una alineación vertical centrada para el párrafo dentro de esa capa.

Existe una solución con CSS: display:table;. Pero tiene el problema habitual de CSS: el excelente soporte de CSS en Internet Explorer, que, para variar, no es compatible con display:table;.

Pero utilizando algunos hacks CSS y metiendo una capa adicional se puede dar solución al centrado vertical, manteniendo compatibilidad con la mayor parte de los navegadores.

Este sería el HTML:

<div class="t1">
 <div class="t2">
  <p>Párrafo a centrar</p>
 </div>
</div>

Y este el CSS:

div.t1 {
 height:12em; /*Altura de la capa*/
 overflow:hidden;
 position:relative;
}

html>/**/body div.t1 {
 display:table;
 position:static;
}

div.t2 {
 position:absolute;
 top:50%;
}
html>/**/body div.t2 {
 display:table-cell;
 position:static;
 vertical-align:middle;
}

p {
 position:relative;
 top:-50%;
}

Ver un ejemplo

Referencias y enlaces relacionados

Datos del artículo:

Información del sitio