Mostrando entradas con la etiqueta accesibilidad javascript. Mostrar todas las entradas
Mostrando entradas con la etiqueta accesibilidad javascript. Mostrar todas las entradas

lunes, 11 de diciembre de 2023

Accesibilidad en SPA (Single Page Application): 3 claves para garantizar una experiencia accesible e inclusiva.

Animación de cargando página

En muchos portales web no navegas realmente cuando pulsas los enlaces de las páginas, sino que todo el contenido de la página, o buena parte del mismo, se modifica dinámicamente. Es posible que ni siquiera te des cuenta... a menos que accedas con un lector de pantalla o solo con el teclado.

La experiencia de una persona ciega en una Single Page Application (SPA) en la que no se ha trabajado la accesibilidad puede ser desastrosa: escucha solo silencio cuando navega, no sabe dónde se encuentra y el foco de teclado se vuelve impredecible.

En este artículo voy a explicar cuáles son las 3 claves para que una Single Page Application (SPA) sea accesible para todas las personas.

Índice

Diferencia entre una Multiple Page Application (MPA) y una Single Page Application (SPA)

Una Multiple Page Application (MPA) y una Single Page Application (SPA) son dos enfoques diferentes para construir portales y aplicaciones web.

El enfoque tradicional y más habitual es el de una Multiple Page Application (MPA), donde cada página se carga por separado desde el servidor cuando el usuario solicita una nueva página. Es decir, cuando navegas de una página a otra, la aplicación realiza una solicitud al servidor para obtener la nueva página completa, de modo que la navegación implica recargar toda la página.

Por el contrario, en una Single Page Application (SPA):

  • Se carga una única página en el navegador del usuario.
  • A medida que interactúas con la aplicación, en lugar de cargar páginas completas desde el servidor, solo se cargan los datos necesarios y se actualiza dinámicamente la interfaz de usuario.
  • La transición entre secciones de la aplicación se realiza de manera suave sin recargar la página completa.
  • Se puede cambiar dinámicamente solo una parte del contenido, por ejemplo, la región principal, pero también en ocasiones todo el contenido de la página.

Por tanto, la principal diferencia radica en cómo se maneja la navegación y la carga de contenido.

En una MPA, cada página requiere una carga completa desde el servidor, mientras que en una SPA, la aplicación se carga una vez y la navegación subsiguiente ocurre de forma más rápida y fluida sin recargas completas de página.

Esquema comparativo entre el ciclo de vida de una página tradicional y una SPA. En una página tradicional se hacen peticiones al servidor y se recargan las páginas. En una SPA las páginas no se recargan, se modifican dinámicamente.

Imagen de Single-Page Applications: Build Modern, Responsive Web Apps with ASP.NET https://learn.microsoft.com/

En resumen, una MPA implica recargar páginas completas al navegar, mientras que una SPA carga la aplicación una vez y actualiza dinámicamente el contenido según las interacciones del usuario.

Un ejemplo tradicional de aplicación web SPA es Gmail, pero también lo son portales como Zara, Yoigo o Pinterest.

Feedback al usuario: incluye mensajes de cargado datos y de página cargada

Cuando accedes a un portal tradicional con un lector de pantalla, este te anuncia el título de la página que se ha cargado. Sin embargo, en una SPA solo hay silencio, porque realmente no se ha cargado la página, solo se ha modificado el contenido dinámicamente.

Para evitar este problema debemos trabajar con mensajes de estado.

Las WCAG no nos obligan a tener mensajes de estado, solo a marcarlos adecuadamente con ARIA para que sean anunciados por el lector de pantalla.

Si te limitas a cumplir estrictamente con las WCAG, las SPA podrían serán inaccesibles con el lector de pantalla. Necesitamos incluir obligatoriamente mensajes de estado.

Las buenas prácticas a seguir son:

  • Incluye un mensaje de estado cada vez que modifiques dinámicamente el contenido de la página. Lo más recomendable es que el mensaje esté visible para todas las personas.
  • Incluye atributos ARIA para que el lector de pantalla anuncie el mensaje aunque no tenga el foco:

    <div role="status" aria-live="polite"><p>Cargando datos de la página... <img alt="" src="spinner.gif"></p></div>

    Mesaje de cargando datos con una animación.

  • A menudo me encuentro que el mensaje de estado solo contiene la animación ... sin texto alternativo, de modo que el lector no tiene nada que anunciar. Si el mensaje es solo una animación, debe tener texto alternativo:

    <div role="status" aria-live="polite"><img alt="Cargando datos de la página..." src="spinner.gif"></div>

  • Igual de importante es anunciar que se están cargando los datos, como que la página ya se ha cargado. Cuando se carguen los datos modifica el mensaje de estado por "La página [título de la página] se ha cargado".

    <div role="status" aria-live="polite"><p>La página 'Proceso de compra. Paso 2 de 3 Dirección de envío' se ha cargado.</p></div>

  • El mensaje de que la página se ha cargado no suele estar visible para todas las personas, solo para las personas que acceden con un lector de pantalla. Recuerda que NO debes ocultar el mensaje con los estilos display:none o visibility:hidden, porque ocultan también el contenido para el lector de pantalla.

Artículos de interés:

Título de la página dinámico: proporciona a cada página un título único y descriptivo

En un portal tradicional MPA, cada página que se carga tiene su propio título de página (en la etiqueta <title> del <head>).

Sin embargo, en una SPA solo hay una página cuyo contenido se modifica dinámicamente, por tanto, aunque te parece que se cargan diferentes páginas, solo es una con su título inicial. Por ello, es imprescindible que se modifique dinámicamente el título de la página (el contenido de la etiqueta <title> del <head>).

Advertencia:

Cambiar dinámicamente el título de la página no provoca que el lector lo anuncie automáticamente como un mensaje de estado.

Por eso seguimos necesitando los mensajes de estado ya explicados.

Es raro encontrar una SPA de acceso público donde no se modifique dinámicamente el título, fundamentalmente por motivos SEO. Por la misma razón, es habitual encontrar errores cuando el título de la página no es relevante para el SEO: por ejemplo, podemos encontrar varias páginas de un proceso de compra que mantienen el mismo título; o que las páginas dentro de la zona de clientes, de acceso restringido, mantienen siempre el título de la página inicial.

El título de la página es importante para una persona usuaria de lector de pantalla, que puede preguntarlo en cualquier momento para ubicarse (normalmente con el atajo del lector: [tecla modificadora, como Insert] + "t").

Un título claro también es importante para cualquier persona: es el que se muestra en la pestaña del navegador, al guardar la página en marcadores, el nombre por defecto al compartir la página en redes sociales, etc.

Hay otra razón por la cuál el título puede ser relevante en una SPA. Ya he indicado que debemos incluir un mensaje de página cargada: "La página [título de la página] se ha cargado". Si utilizamos el contenido de la etiqueta <title> para generar dinámicamente el mensaje de página cargada, será muy confuso cuando el lector de pantalla anuncie que se han cargado diferentes páginas siempre con el mismo nombre:

Incorrecto: "La página 'Proceso de compra' se ha cargado", "La página 'Proceso de compra' se ha cargado", "La página 'Proceso de compra' se ha cargado" ...

Un título único y descriptivo para cada página provocará mensajes de estado más claros cuando se utiliza el título para generarlos:

Correcto: "La página 'Proceso de compra. Paso 1 de 3. Datos del cliente' se ha cargado", "La página 'Proceso de compra. Paso 2 de 3 Dirección de envío' se ha cargado", etc.

Control del foco de teclado

El tercer gran error que me suelo encontrar en las auditorías de portales o aplicaciones web SPA es el manejo del foco. Ten en cuenta que el usuario no sabe que se encuentra en una SPA ni tiene por qué saberlo.

Hay muchas personas que acceden a las páginas mediante pulsadores o solo con el teclado, entre ellas las personas que usan un lector de pantalla, muchas de las cuales son ciegas y no pueden ver dónde está el foco.

En un portal tradicional, cuando la página se carga, el foco de teclado se sitúa al comienzo de la página.

En una SPA donde no se ha trabajado la accesibilidad es muy confuso manejarte con el foco de teclado si no puedes ver la pantalla porque, a menudo, resulta impredecible saber a dónde irá el foco. Cuando se carga nuevo contenido, quizás el foco de teclado se pierda debido a la ausencia de un elemento que ya no está presente; o tal vez se quede donde está, aunque se haya anunciado la carga de una nueva página y tú esperes que el foco del teclado se encuentre ahora al inicio de la misma.

Por tanto, la tercera clave para que una SPA sea accesible es manejar correctamente el foco de teclado cuando el contenido se carga.

Para hacerlo bien impera el sentido común, conocer cómo acceden las personas usuarias de lector de pantalla y de teclado; e involucrarlas siempre que se pueda en las pruebas.

Opción 1. Mandar el foco de teclado al comienzo de la página

Si se está modificando dinámicamente todo el contenido central de la página, una opción puede ser mandar el foco al comienzo de la página, que es lo que el usuario espera. En concreto, sitúalo en el primer enlace del <body>: en el enlace "Saltar al contenido".

De este modo, después de que el mensaje de estado anuncie "La página [título de la página] se ha cargado", el foco se sitúa siempre de manera predecible al comienzo de la página, en el enlace para saltar al contenido central.

El usuario de lector de pantalla tiene el feedback necesario y es un comportamiento predecible.

El usuario de teclado que ve la página y no usa un producto de apoyo, no se queda con el foco en un lugar donde le resulte difícil regresar al inicio de la página o a la región principal. Por ejemplo, no se queda con el foco de teclado en el pie de página tras pulsar uno de sus enlaces.

Opción 2. Mandar el foco de teclado al título de la página

En otros casos, puede ser más usable mandar el foco al título de la página (al <h1> en el <main>).

Os pongo un ejemplo muy claro. Estas son dos pantallas de un curso accesible (de ejemplo) de isEazy, en concreto la página de inicio y una página interior:

Captura de la portada de un curso accesible de isEazy. En la cabecera está el botón de menú, el título del curso y el progreso. En el contenido está el título, una imagen y un botón Comenzar curso
Captura una página interior del curso del portal de la imagen anterior. Tiene la misma cabecera. Al final del contenido hay un botón Volver arriba y un botón Siguiente sección.

isEazy genera automáticamente la versión accesible de un curso como una SPA. Por tanto, cuando pulsas "Comenzar curso" o "Siguiente sección" no se navega a otra página, sino que se modifica dinámicamente el contenido de la página.

Teniendo en cuenta los elementos concretos de la cabecera del curso y que el objetivo del alumno es ir avanzando por los contenidos del mismo, la conclusión a la que llegamos en este proyecto fue que la mejor alternativa era enviar el foco al título de la página (al <h1> en el <main>). De este modo, el usuario avanza de manera rápida, fluida y consistente. También puedes simplificar el mensaje de cargando página a "La página se ha cargado" o "El contenido se ha cargado", puesto que a continuación coge el foco el título de la página (el <h1>), por lo que el lector ya lo va a anunciar y no es necesario repetirlo en el mensaje de estado.

Cuando envíes el foco de teclado a un elemento ten en cuenta estos dos puntos:

  • Si el elemento que coge el foco NO es un elemento de interacción, como en este caso el <h1>, debe tener el atributo tabindex="-1", nunca tabindex="0". Si le añades tabindex="0" cogerá el foco al tabular por la página. Si le añades tabindex="-1" solo cogerá el foco por programación, pero nunca al tabular por la página, que es lo correcto.
  • La indicación visual de que tiene el foco debe ser clara. Evita limitarte a un simple cambio de color; por ejemplo, podría estar resaltado mediante un recuadro.

Opción 3. Mantener el foco de teclado en el elemento de interacción pulsado

En otros casos, cuando se modifica dinámicamente solo una parte del contenido, mover el foco al comienzo de la página o al título no es la mejor solución.

Por ejemplo, puedes tener un buscador en la región principal, de modo que solo se recargan los resultados de la búsqueda:

Captura de una página del buscador de tiendas de Vodafone. El foco de teclado está en el botón Buscar tienda. Debajo se han recargado dinámicamente los resultados con un mensaje previo Mostrando 9 tiendas Vodafone

En este caso, puede ser más adecuado que:

  • el foco se quede en el botón "Buscar", como se muestra en el ejemplo anterior;
  • el mensaje "Mostrando 9 Tiendas" tenga los atributos ARIA de mensaje de estado que hemos comentado. De este modo, el lector de pantalla anunciará el mensaje aunque no se haya movido el foco. La siguiente tabulación nos llevará al primer resultado de la búsqueda.

Otras recomendaciones

Otras recomendaciones a tener en cuenta a la hora de plantear la accesibilidad de una SPA son:

  • Acuérdate de modificar la miga de pan convenientemente para que refleje en qué página estamos.
  • Si marcas el menú o paso actual, acuérdate de marcar el correcto cuando modifiques el contenido.
  • Tendrás que programar un retardo para que el lector de pantalla anuncie todo adecuadamente. Debemos asegurarnos de que el lector de pantalla carga el nuevo contenido en el búfer; y que le da tiempo a leer el mensaje de estado antes de leer el contenido que coge el foco, sin interrupciones ni cambios abruptos.

Si quieres añadir algún consejo más puedes proponerlo en los comentarios.

Esta no es la solución...

Una SPA puede suponer un grave problema de accesibilidad para las personas usuarias de lector de pantalla si no se trabaja correctamente la accesibilidad, pero si se siguen las pautas de accesibilidad que he dado, se genera una gran experiencia de usuario compatible con sus productos de apoyo.

Algunos portales tienen la tentación de solucionarlo incluyendo una overlay o widget de accesibilidad. En vez de hacer que su portal sea compatible con los productos de apoyo habituales de los usuarios, les obligan a usar un componente que incorporan en su portal y que no soluciona el problema.

Es como si al entrar a Aragón te obligáramos a bajarte de tu coche porque nuestras carreteras no están asfaltadas, pero hemos invertido unos millones de euros en comprar burros que ofrecemos a nuestros visitantes. Mejor invierte el dinero en asfaltar las carreteras.

Artículos relacionados:

viernes, 17 de julio de 2020

Reseña del libro "Designing with progressive enhancement"

Portada del libro 'Designing with progressive enhancement. Building the web that works for everyone.' de Todd Parker

Autores: Todd Parker, Patty Toland, Scott Jehl, Maggie Costello Wachs

Nº páginas: 428

Idioma: inglés

Formato: digital e impreso

Fecha de publicación: 2010

Web: Ficha en Filament Group

Podéis consultar más reseñas de libros en: "Libros y reseñas"

Recomiendo este libro a cualquier desarrollador front-end, de hecho les animaría a todos a que lo leyeran. El libro tiene 10 años, pero sigue plenamente vigente.

Es una guía práctica que enseña a implementar páginas con una gran interactividad, de manera que el mismo código funciona correctamente en cualquier navegador antiguo o moderno, sea cual sea la configuración del usuario, e independientemente de su contexto de acceso o los productos de apoyo que utilice.

Para lograrlo, el libro enseña a desarrollar mediante el enfoque de "progressive enhancemente" (mejora progresiva) que supone dejar atrás malos hábitos y adoptar una perspectiva diferente de diseño y desarrollo.

Hay muchos contextos de acceso en los que nos vamos a beneficiar de una implementación que siga un enfoque de mejora progresiva:

  • acceso con un dispositivo o un navegador antiguo, a veces impuesto por la empresa;
  • acceso con dispositivos o navegadores actuales pero con capacidades limitadas, como el navegador de Kindle;
  • situacionales, por ejemplo, si desactivamos ciertas capacidades por:
    • seguridad: plugins, JS...
    • por conectividad: imágenes, vídeos...;
  • si hay un problema en la conexión o en el servidor que provoque que se cargue la página sin ficheros CSS, JS, etc.
  • personas con menor experiencia tecnológica, por ejemplo, si se requiere la instalación o actualización manual de plugins (Flash, Silverlight, Sun Java...), que además pueden no ser compatibles con todas las plataformas o navegadores;
  • las personas que acceden con productos de apoyo también se van a beneficiar de que las páginas se implementen con el enfoque que voy a resumir.

La metodología es sencilla y la explican con mucho detalle. Uno de los puntos fuertes del libro es que incluye numerosos ejemplos de maquetación y programación, paso a paso, de diferentes widgets.

Partimos de un código HTML correcto y semántico, formado por elementos estáticos estándar y controles de formulario, al que aplicamos una CSS de estilos básicos que trabajan correctamente en todos los navegadores. Esto nos proporciona una página completamente funcional, usable y accesible desde el principio, que funciona en cualquier dispositivo, navegador o contexto de acceso.

Una vez que tenemos la experiencia básica, la página llama al framework EnhancedJS, un JS gratuito que testea las capacidades del navegador. Si pasa todos los test, se cargan los ficheros CSS y JS que añaden una capa de diseño e interacción que mejoran la experiencia para los navegadores que la soportan, haciéndola más rica e interactiva.

Esta capa de mejora se implementa de manera que sea accesible por teclado y accesible con un lector de pantalla. Además, EnhancedJS incluye un enlace en la página para que el usuario pueda intercambiar de forma manual entre la versión básica y la versión avanzada.

Hay una serie de aspectos claves que subyacen a este enfoque, entre los que destacaría:

  • la separación entre el contenido, la presentación y el comportamiento;
  • el uso de JavaScript no intrusivo;
  • la maquetación HTML semántica;
  • el uso de WAI-ARIA.

No hay que confundir la "mejora progresiva" con la "degradación elegante" (graceful degradation).

Un ejemplo de degradación elegante sería el uso de la etiqueta <noscript>. En este enfoque se implementa la solución teniendo en cuenta a los usuarios con los dispositivos y navegadores más modernos, y luego se da una solución alternativa a los usuarios con navegadores más antiguos.

Por el contrario, en la mejora progresiva, siempre más recomendable, no sería necesario el uso de una etiqueta <noscript>, porque tendremos una página que trabaja sin JS y sin CSS, que es soportada por todos los navegadores y dispositivos, pero que ofrece una experiencia avanzada y accesible para los dispositivos y navegadores que la soportan.

Árbol desplegable con iconos de carpetas. La versión sencilla es una lista anidada desplegada.

Mejora progresiva. Ejemplo de un árbol desplegable en la versión mejorada y en la versión básica.

El libro de divide en dos partes:

  • En la primera parte repasa la metodología, esta manera diferente de pensar la planificación de los diseños. Tiene capítulos específicos sobre las buenas prácticas para implementar HTML semántico; aplicar CSS de forma adecuada; añadir JavaScript no intrusivo; y usar el framework EnhancedJS, que testea la capacidad del navegador antes de aplicar la capa avanzada de mejoras.
  • En la segunda parte hay doce capítulos, cada uno tiene el ejemplo de la implementación de un widget diferente mediante mejora progresiva.

Primera parte. Mejora Progresiva

Capítulo 1. Nuestro enfoque

En este capítulo explican el enfoque de mejora progresiva, tal y como lo he resumido previamente.

Como he indicado, se tienen dos experiencias, pero una única página y un único código. Hay una experiencia básica que funciona universalmente en todos los dispositivos, y una mejorada que se entrega a los navegadores con más funciones.

Las claves son:

  • Todos los componentes están basados en HTML semántico y bien estructurado, de tal manera que la funcionalidad para una experiencia básica está disponible sin CSS y sin JS. Sobre esta experiencia básica se construye lo demás.
  • Habrá una estricta separación entre contenido, presentación y comportamiento.
  • Se testea las capacidades CSS y JS del navegador antes de aplicar las mejoras o mantener la experiencia básica.
  • En los navegadores que admiten la experiencia mejorada, se añadirá la capa no intrusiva de estilos y comportamiento, que será accesible por teclado y por los lectores de pantalla.

Introducen el concepto "the x-ray perpective" que desarrollan en el siguiente capítulo y que hace referencia a cómo analizar el diseño de una interfaz compleja, por ejemplo, una aplicación web para organizar fotos.

La "perspectiva de rayos X" toma el diseño de una interfaz web compleja y lo descompone en sus diferentes partes, para encontrar el elemento HTML nativo adecuado para la experiencia básica. Por ejemplo, en un árbol desplegable, sería una lista UL anidada.

Consiste por tanto en pensar de forma creativa cómo deconstruir esos widget e interacciones en elementos HTML nativos estándar que puedan hacer la misma tarea. Mapear el HTML semántico que soportará la experiencia con la funcionalidad básica, y planear cómo se desarrollará la capa avanzada, con CSS y JS, para crear la experiencia mejorada, accesible además en el acceso por teclado y con el lector de pantalla.

Capítulo 2. X-ray perspective

Este capítulo incluye diversos ejemplos de la "perspectiva de rayos X", es decir, de la descomposición de una interfaz con una interacción y un diseño complejo, en componentes básicos y estándares de HTML a partir de los cuales poder construir la experiencia mejorada.

Partimos de la base de que hasta el diseño dinámico más complejo se puede descomponer en contenidos y funcionalidades esenciales expresados en elementos de HTML simples y semánticos, que proporcionarán una experiencia usable y accesible para todos los visitantes. Es un ejercicio muy divertido y creativo que todos deberían probar.

Si queremos tener, por ejemplo, una aplicación web para organizar fotos, pensamos en su versión más avanzada y seguimos un proceso que explican a lo largo del capítulo:

  • Definimos la jerarquía y la prioridad del contenido.
  • Mapeamos los componentes en sus equivalentes en HTML básico y semántico.
  • Construimos el marcado base que proporciona todo el contenido y toda la funcionalidad esencial, con una CSS básica y sin JS.
  • Escribimos la capa de mejora visual y funcional con CSS y JS en los navegadores que la soporten.

Una vez hecho esto, pasamos la siguiente checklist de verificaciones:

  • ¿La experiencia básica es totalmente funcional y usable solo con HTML?
  • ¿El marcado de base codifica toda la información, incluido el diseño y la estructura, que necesita la experiencia mejorada?
  • ¿Tanto en la experiencia básica como en la experiencia avanzada, la pagina se lee y trabaja en un orden lógico y promueve el contenido y la funcionalidad más importante?
  • ¿Son todas las páginas navegables y todos los elementos de formulario accesibles usando solo el teclado?
  • ¿Las paginas tienen sentido al navegar con el lector de pantalla? ¿puede el usuario navegar por la estructura de encabezados?
  • ¿Hay un opción clara para todos los usuarios (incluidos dispositivos móviles y lector de pantalla) para cambiar entre la experiencia básica y la experiencia avanzada? Por ejemplo, podemos querer acceder a la versión básica en función de la conexión, las preferencias o si por alguna razón la versión mejorada falla?

Capítulo 3. Escribir código HTML semántico

Este capítulo es un repaso de cómo implementar correctamente HTML de forma semántica. Trata los encabezados, párrafos y listas; el uso de BR y el abuso de DIV y SPAN; las citas, el texto enfatizado y las abreviaturas; las tablas, las imágenes y los elementos interactivos (enlaces y formularios); las zonas semánticas de la página (cabecera, pie, zonas de navegación...); o el contenido del head.

Por último, hace una enumeración básica de los requisitos de accesibilidad de las WCAG relacionados.

Capítulo 4. Aplicar estilos de forma efectiva

Se revisan las buenas prácticas relativas a las técnicas que comúnmente se emplean para aplicar CSS en el enfoque de mejora progresiva:

  • la importancia de usar ficheros CSS y en el menor número posible, salvo excepciones muy concretas en que sí puede ser mejor usar el atributo style;
  • los inconvencientes de usar @import;
  • los diferentes tipos de CSS;
  • las recomendaciones para usar convenciones en el nombre de las CSS;
  • sobre los comentarios condicionales;
  • etc.

Como ya he explicado previamente, habrá una CSS básica con estilos "seguros" (basic.css) y una CSS avanzada (enhacenced.css),  por lo que hay que decidir qué estilos son básicos y cualés avanzados para saber en cuál de estas CSS incluirlos.

Por ejemplo, serán seguros para todos los navegadores los estilos que hacen referencia a la fuente o al estilo del texto; pero los relacionados con la posición, las cajas flotantes, las dimensiones o los márgenes, deberían estar en la CSS avanzada.

Una ventaja añadida de tener una CSS básica es que nos sirve también como CSS de impresión, simplemente habrá que tener cuidado en ocultar los bloques que no se deben imprimir, como la navegación.

También incluyen consideraciones de accesibilidad:

  • la definición de la fuente en medidas relativas; 
  • el contraste de color; 
  • evitar la información que se transmite solo por el color; 
  • la necesidad de subrayar los enlaces dentro del texto; 
  • definir los colores de fondo bajo las imágenes de fondo; 
  • no ocultar el foco;
  • el ancho de columna recomendado; u
  • ocultar correctamente el contenido.

Por último, repasa diversos trucos para dudas habituales relacionadas con los elementos flotantes o los bugs en IE, como los relacionados con el z-index o el hasLayout.

Capítulo 5. Mejorar la experiencia y la interactividad con scripts

Este capítulo de centra en cómo aplicar JS no intrusivo para extender la funcionalidad de las páginas: 

  • Aplicar el código JS al HTML de manera correcta: dónde y cuándo cargar los scripts.
  • La inclusión del JS EnhancedJS para testear las capacidades del navegador y cargar los JS y CSS para la experiencia mejorada.
  • Cómo construir el marcado mejorado y añadir la interactividad a los controles por JS no intrusivo. Por ejemplo, aprovechando el texto existente en el HTML de base; controlando correctamente la ocultación de contenido con la técnica adecuada para cada caso; etc.
  • La importancia de conservar y mejorar la usabilidad y accesibilidad, asegurando el acceso por teclado y el acceso mediante lector de pantalla, para lo cual será imprescindible el correcto uso del atributo tabindex y el estándar WAI-ARIA (artículo relacionado WAI-ARIA).

Capítulo 6. Testear la capacidad del navegador

El framework javascript EnhancedJS testea las capacidad CSS y JS del navegador. Si pasa todos los test y, por tanto, soporta todas las características, se cargan las CSS y JS de la capa mejorada; de lo contrario, se mantiene la experiencia básica que es totalmente funcional.

No solo testea si se soportan ciertos elementos, sino si los soporta correctamente, en especial en aspectos como el modelos de cajas y sus dimensiones o posiciones. Por ejemplo, dada una caja con unas dimensiones (ancho, márgenes, padding, …) compara su ancho final con la suma de sus dimensiones.

Cuando el test pasa:

  • Asigna la clase "enhanced" a los elementos HTML. De este modo, por ejemplo, en la CSS avanzada defines el estilo de HMTL.enhanced {} y en la CSS básica el estilo de HTML {}.
  • Se incluyen las CSS y JS especificados para la experiencia mejorada.
  • Graba el resultado en una cookie para prevenir al framework del resultado del test en cada pagina, así primero busca si existe la cookie.
  • Añade un enlace en la página para que el usuario pueda intercambiar de forma manual entre ambas versiones. Tiene opciones de personalización y se puede deshabilitar.

También se puede forzar a pasar o fallar el test; a borrar la cookie y refrescar la pagina; o hacer que el valor de la cookie sea "pasa" o "falla" y refrescar.

Para incluir el framework:

  • Se añade la llamada a la función "enhance()" en cada página. Acepta diferente parámetros, como el listado de JS y CSS que quieres añadir en la versión mejorada. Si el navegador no soporta JavaScript o no lo tiene activado, se ignora la función y se carga la versión básica. Los autores indican también un método para que no haya un flash de la pagina sin estilos mientras se carga la versión avanzada.
  • La inclusión del fichero "enhance.js" que realiza todos los test, que pueden ser editados y extendidos fácilmente.

Hay otras librerías que podrían ser complementarias a esta, como modernizr.

Segunda parte. Mejora Progresiva en acción

Esta segunda parte incluye los ejemplos sobre cómo construir diferentes widget desde cero, paso a paso, mediante mejora progresiva.

Cada capítulo es un ejemplo diferente, pero todos tienen la misma estructura:

  • Se parte del resultado final, la versión mejorada interactiva a la que se quiere llegar, revisándolo desde la perspectiva de rayos x (x-ray perspective).
  • Se mapea para descomponer sus piezas funcionales en elementos HTML estándar.
  • Se escribe el marcado base.
  • Se aplican estilo "seguros" en una CSS básica.
  • Se crea la capa de mejora con CSS avanzadas y ficheros JS.
  • Se asegura la accesibilidad por teclado y con el lector de pantalla.

Todos los ejemplos están disponibles en la web del libro:

Podéis pulsar el enlace "View high-bandwidth version" / "View low-bandwidth version" que hay al final de cada página de ejemplo para ver la versión avanzada o básica.

Para evitar que la página se vea sin estilos mientras se testea el navegador y se carga la versión avanzada, propone:

html.enhanced body {visibility: hidden}

y una vez cargada

html.enhanced body {visibility: visible}

Todos los ejemplos en:

Artículos relacionados:

jueves, 14 de noviembre de 2013

Live Regions y WAI-ARIA. Cómo mejorar la accesibilidad de contenidos que se actualizan automáticamente

Última actualización: 01/08/2017, he añadido la novedad de WAI-ARIA 1.1, los Live Regions Roles (alert, log, marquee, status, timer)

En este artículo explico:

  • el problema de accesibilidad que pueden suponer las zonas de nuestras páginas que se añaden, eliminan, actualizan o modifican automáticamente sin intervención del usuario (llamadas Live Regions)
  • lo fácil que es solucionar este problema con el uso del atributo aria-live de WAI-ARIA en dicha región.
  • el uso correcto del atributo aria-live y otros relacionados: aria-atomic, aria-relevant y aria-busy.
  • incluyo un ejemplo detallado para que podáis ver las diferencias según los valores que indicamos en estos atributos
  • la compatibilidad con diferentes lectores de pantalla y navegadores

Comprender el problema para entender la solución

Imaginemos que tenemos una zona o varias zonas de nuestra página web cuyo contenido se añade, elimina, actualiza o modifica sin intervención del usuario. Es lo que llamamos una live region, una zona viva de la página.

Os enumero algunos ejemplos, podemos tener una zona en la cual...

  • ... se actualicen los resultados de determinados eventos deportivos, por ejemplo, cada vez que un equipo marca un gol,
  • ... haya un banner publicitario que vaya mostrando cada x minutos un anuncio diferente,
  • ... haya un contador, por ejemplo indicando los minutos que restan para que termine el tiempo de un examen,
  • ... se muestren mensajes importantes al usuario, por ejemplo errores de validación de un formulario a medida que el usuario rellena los campos del mismo,
  • ... se muestren los usuarios que están conectados en ese momento, por ejemplo los alumnos en línea en un curso online,
  • ... se muestren los nuevos mensajes de un chat, de Twitter o las últimas noticias desde una fuente RSS.

Podríamos seguir poniendo ejemplos, lo que tienen en común todas ellas es que se actualizan automáticamente mediante un evento externo, no porque el usuario lo solicite explícitamente, y cuando se realiza dicha actualización el usuario puede tener el foco en cualquier otra parte de la página.

Si puedes ver la página seguramente no tendrás problemas para percatarte del cambio que se efectúe en una live region. El problema surge cuando accedes a la página mediante un lector de pantalla, pues no serás conscientes de la actualización a menos que el lector de pantalla te la anuncie.

Este es por tanto el problema que queremos solucionar: queremos que el lector de pantalla pueda anunciarle al usuario que se ha producido un cambio en una zona dinámica de la página (live region), qué cambio ha sido y decidir cómo y cuándo queremos que se lo anuncie.

No está de más recordar que es además un requisito de accesibilidad necesario para alcanzar el nivel de adecuación A de acuerdo a las WCAG 2.0. El criterio de conformidad 4.1.2 Nombre, función, valor (nivel A), indica:

4.1.2 Nombre, función, valor: Para todos los componentes de la interfaz de usuario (incluyendo pero no limitado a: elementos de formulario, enlaces y componentes generados por scripts), el nombre y la función pueden ser determinados por software; los estados, propiedades y valores que pueden ser asignados por el usuario pueden ser especificados por software; y los cambios en estos elementos se encuentran disponibles para su consulta por las aplicaciones de usuario, incluyendo las ayudas técnicas. (Nivel A)

WAI-ARIA como solución

WAI-ARIA (WAI - Accessible Rich Internet Applications) es una especificación del W3C que proporciona una ontología de roles, estados y propiedades que se pueden utilizar para mejorar la accesibilidad y la interoperabilidad de los contenidos y aplicaciones web.

WAI-ARIA provides a collection of accessibility states and properties which are used to support platform accessibility APIs on various operating system platforms. Assistive technologies may access this information through an exposed user agent DOM or through a mapping to the platform accessibility API. When combined with roles, the user agent can supply the assistive technologies with user interface information to convey to the user at any time. Changes in states or properties will result in a notification to assistive technologies, which could alert the user that a change has occurred.

Accessible Rich Internet Applications (WAI-ARIA) 1.0

WAI-ARIA nos proporciona pues mecanismos para transmitir a los productos de apoyo la información sobre la función, los estados y las propiedades (y sus valores) de los componentes de interacción personalizados, así como las notificaciones sobre los cambios producidos en los mismos.

Los atributos de WAI-ARIA que nos permitirán identificar las live regions y definir cómo queremos que se anuncien las modificaciones que en ellas se llevan a cabo son:

  • aria-live: para identificar la live region y, mediante su valor, indicar cuándo queremos que se anuncien sus actualizaciones al usuario.
  • aria-atomic: para definir si queremos que se anuncie toda la región o solo las partes que han cambiado.
  • aria-relevant: para establecer qué tipo de actualización en la región queremos que se anuncie al usuario.
  • aria-busy: para detener o activar temporalmente el anuncio de la actualización cuando muchas partes de un mismo elemento van a ser modificadas, y de este modo esperar a que terminen de realizarse las modificaciones antes de anunciar el cambio.

Cómo usar correctamente aria-live, aria-atomic, aria-relevant y aria-busy

aria-live

Este atributo se añade a un elemento para indicar que es una live region, es decir, que su contenido se modifica y actualiza dinámicamente.

Mediante el valor del atributo podemos indicar cuándo queremos que el lector de pantalla anuncie los cambios al usuario:

  • aria-live="off": los cambios se anunciarán al usuario solo cuando el foco esté en esa región. Será adecuado usarlo cuando la actualización no es relevante, por ejemplo una rotación de imágenes en la cabecera.
  • aria-live="polite": los cambios se anunciarán al usuario pero sin interrumpirle, anunciará el cambio cuando termine la tarea que está llevando a cabo, por ejemplo cuando termine de escribir en un campo o termine de leer un contenido.
  • aria-live="assertive": los cambios se anuncian de inmediato, independientemente de lo que el usuario esté haciendo. Teniendo en cuenta que el anuncio puede desorientar a los usuarios o que estos no terminen la tarea que están realizando, solo debe utilizarse para mensajes y advertencias importantes, por ejemplo los mensajes de error de validación en un formulario.

aria-atomic

Con aria-atomic especificamos si queremos que se anuncie toda la región o solo las partes de la misma que han cambiado.

Si el valor del atributo es "true" el lector de pantalla anunciará la región como un todo, por el contrario, si es "false" (el valor por defecto) solo anunciará el nodo que ha cambiado.

aria-relevant

Con este atributo indicamos qué tipo de actualización de la live region deseamos que se anuncie al usuario:

  • aria-relevant="additions": anuncia los elementos que se añaden al DOM. Si el contenido que se añade es semánticamente relevante será importante anunciarlo, por el contrario, si es meramente decorativo no tiene sentido que se anuncie.
  • aria-relevant="removals": anuncia los elementos que se eliminan del DOM. Igual que en el caso anterior, es importante tener en cuenta si el elemento eliminado es relevante o solo decorativo. Por ejemplo, en un listado de amigos en línea, si un amigo se desconecta sería interesante anunciar que se ha eliminado de la lista.
  • aria-relevant="text": anuncia las modificaciones de texto.
  • aria-relevant="all": anunciará todo, es decir, los elementos añadidos, eliminados y las modificaciones de texto.

Si no se incluye el atributo aria-relevant, por defecto se anunciarán los nodos añadidos y las modificaciones de texto.

aria-busy

Por defecto su valor es "false". Este atributo se utiliza cuando muchas partes de un mismo elemento van a ser modificadas, entonces puedes poner el valor a "true" para que temporalmente no anuncie las modificaciones y, una vez que se hayan llevado a cabo, volver a ponerlo a "false" para que las anuncie.

Ejemplo ARIA: Live Region

Nuestra live region es una zona de "Últimas noticias" que se actualiza de forma automática, sin intervención del usuario. Cada 10 segundo se elimina la última noticia de la lista y se incluye una nueva al comienzo de la misma.

La página está en HTML5, pero no es obligatorio, puedes utilizar WAI-ARIA en (X)HTML también. El código HTML de la live region es:


<div id="#contenedor" role="complementary">
   <div id="noticias">
   <h2 id="liveLabel">Últimas noticias</h2>
   <p class="peq">Se actualizan cada 10 segundos</p>
           <div id="liveDiv"  role="log" aria-labelledby="liveLabel" 
                 aria-live="assertive" 
                 aria-atomic="true" 
                 aria-relevant="additions">
              [5 div con cada una de las noticias]
          </div>
    </div>
[...]
</div>           

Como se observa, al DIV que contiene las noticias se le han añadido los atributos:

  • aria-live="assertive", indica que es una live region y que queremos que anuncie sus modificaciones de forma inmediata.
  • aria-atomic="true", indica que queremos que nos anuncie toda la región. NVDA y JAWS nos leen la zona completa de noticias, es decir, las cinco noticias. Si lo hubiéramos puesto a "false" solo nos leería la nueva noticia (el nodo completo: título, fecha y descripción)
  • aria-relevant="additions", indica que queremos que nos lea solo los elementos que se insertan en el DOM, por ello nos lee solo la noticia que insertamos pero no la que eliminamos de la lista.

Al no incluir aria-busy, su valor por defecto es "false".

El rol aplicado es role="log", que identifica una región donde nueva información es añadida en un orden significativo y la información vieja puede desaparecer. Este rol implica que hay una relación entre la llegada de nuevos elementos y el orden de lectura, la información se añade solo al final y no en puntos arbitrarios. Los elementos con este rol tienen implícito un aria-live="polite"

Comprobar las diferencias según los valores definidos en los atributos

Para facilitar que podáis comprobar cómo cambia la forma de anunciarse la actualización de las noticias en función del valor de cada atributo, hay un formulario para que podáis modificar estos valores.

Los nuevos valores de los atributos se aplican a la live region sin necesidad de recargar la página.

En el caso de aria-busy, podéis modificar el valor para comprobar cómo anuncia o no los cambios según esté a "true" o a "false", pero hay que tener en cuenta que esto habría que programarlo dinámicamente, para que, mientras se llevan a cabo múltiples actualizaciones de una región dejará de anunciar sus cambios hasta que se completaran todas.

¿Cómo puedo comprobar el resultado?

Si tienes instalado un lector de pantalla solo tienes que abrir la página de ejemplo "Ejemplo ARIA: Live Región" y escuchar. Cada vez que se actualice una noticia te leerá la zona de noticias independientemente de dónde tengas el foco.

Si eres un desarrollador y no tienes instalado un lector de pantalla, puedes instalarte uno gratuito como NVDA o con versión de prueba como JAWS. Pero también puedes instalarte la extensión de Chrome ChromeVox que es muy útil para probar desarrollos con WAI-ARIA.

Una vez instalada la activas en "Herramientas > Extensiones" y abres la página del ejemplo: "Ejemplo ARIA: Live Región", podrás comprobar cómo te anuncia cada actualización de la zona de "Últimas noticias" según los valores definidos para los atributos de la región.

También es muy útil la extensión Accessibility Developer Tools, te permitirá evaluar si tu código ARIA es correcto. Para ello, una vez instalada, accedes a "Herramientas > Herramientas para desarrolladores > Audits" y haces la auditoría con la opción de "Accessibility" seleccionada. Te indicará por ejemplo si falla alguna propiedad ARIA o su valor no es adecuado.

Compatibilidad y diferencias entre diferentes lectores de pantalla

Funciona bastante bien con JAW15 corriendo en Firefox 25 y Explorer 10, sin embargo no funciona con JAW15+Chrome.

También funciona con VoiceOver en Chrome y Safari.

Con NVDA 2013 solo va cuando corre bajo Firefox 25, y además solo con aria-atomic="false", si el valor es "true" no lee nada. No funciona corriendo en Chrome y Explorer.

Hay una diferencia en cómo anuncia ChromeVox y JAWS los cambios de la región cuando aria-atomic="true". JAWS lee toda la región, es decir, todas las noticias. Sin embargo ChromeVox nos anuncia el título de la región, como está asociada al titulo "Últimas noticias" mediante el atributo aria-labelledby nos anuncia que se ha modificado "Últimas noticias".

Por otra parte ChromeVox parace ser el único que anuncia correctamente los nodos eliminados cuando aria-relevant es "removals" o "all", el resto de lectores no anuncian los nodos eliminados.

Si probáis con otros navegadores y/o lectores de pantalla os agradecería que pusierais vuestra experiencia en los comentarios.

Live Regions Roles (alert, log, marquee, status, timer)

¿Qué diferencia hay entre marcar un mensaje de error de estas tres maneras?

<p id="errors3" role="alert" aria-atomic="true"></p>

<p id="errors3" aria-live="assertive" aria-atomic="true"></p>

<p id="errors3" role="alert" aria-live="assertive" aria-atomic="true"></p>

Puedes ver un ejemplo en la página: Simple Form Validation Using Only WAI-ARIA role=alert or aria-live=assertive

role=alert funciona como aria-live con la ventaja de que precede el anuncio de la palabra "alerta".

Se puede usar sin aria-live y se le pueden añadir los atributos aria-atomic, aria-busy y aria-relevant

La ventaja de usarlo con aria-live es que podemos indicar cuándo queremos interrumpir al usuario y aumentamos la compatibilidad con los navegadores. aria-live tiene mejor soporte en JAWS y ChromeVox que en NVDA, donde funciona mejor con Firefox. role=alert está también soportado por IE.

La nueva versión de WAI-ARIA (la 1.1), trae una novedad, define un nuevo tipo de roles, los Live Regions Roles, es decir, aquellos a los que se pueden aplicar los atributos de las live regions:

  • role="alert": contiene información relevante y se anuncia con “alerta”. Debería ser aria-live="assertive"
  • role="log": donde la información se agrega en orden significativo y la antigua puede desaparecer, debería ser aria-live="polite"
  • role="marquee": información no relevante que cambia con frecuencia, debería ser aria-live="off"
  • role="status": advertencia no lo suficientemente importante para justificar una alerta, debería ser aria-live="polite"
  • role="timer": contador numérico del tiempo transcurrido o restante

Referencia en WAI-ARIA 1.1: 5.3.5 Live Region Roles

WAI-ARIA 1.1 es recomendación desde el 14/12/2017. Puedes consultar todas las novedades en el artículo: Novedades WAI-ARIA 1.1

Enlaces de interés

Artículos relacionados

Servicios de accesibilidad que ofrezco como consultora freelance

miércoles, 28 de marzo de 2012

Descripción extensa de una imagen: accesible con lector de pantalla y visible sin imágenes activas

El objetivo de este artículo es explicar cómo podemos tener una descripción extensa de una imagen en la propia página, de tal manera que:

  • sea accesible para los lectores de pantalla
  • se visualice cuando la imagen no esté cargada (bien porque tenemos las imágenes desactivadas, bien porque ya no se encuentra disponible, etc.)

Todo ello sin depender del atributo longdesc, que no siempre es soportado, ni de los típicos enlaces [D] junto a la imagen (conocidos como D-Link); y asegurando que no dé problemas cuando se deshabilita el javascript o las CSS.

Este artículo se me ocurrió después de leer el artículo de Steve Faulkner Detecting if images are disabled in browsers, en el cual explicaba cómo podemos detectar por javascript si las imágenes están deshabilitadas o no se han cargado.

Ver la página de ejemplo

Descargar el ejemplo (RAR, 13Kb)

La descarga y uso del ejemplo es completamente libre, pero os agradecería mucho que compartierais mejoras que se os ocurran o problemas que detectéis.

He probado su funcionamiento con NVDA, Explorer 7, Explorer 8, Explorer 9, Firefox 1.5, Firefox 2, Firefox 3, Firefox 11, Chrome 17, Opera 11, Safari 5 (para Windows) y desde una BlackBerry y una iPad. En Explorer 6 no hay problema para visualizar la descripción, pero se ve siempre presente como si el javascript estuviera deshabilitado.

HTML

El código HTML es XHTML 1.0 Strict válido y es el siguiente. En primer lugar tenemos la cabecera:


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" >
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="es" lang="es">
<head>
<title>Ejemplo descripción extensa accesible de imágenes</title>
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1" />
<meta name="author" content="Olga Carreras" />
<meta name="description" content="Descripciones extensas de imágenes accesibles con lectores de pantalla y sin imágenes activas" />

<link href="estilos.css" type="text/css" rel="stylesheet" media="all" />
<script type="text/javascript" src="utils.js"></script>
</head>

Como se ve, estoy llamando a una CSS y a un JS que explico más adelante.

A continuación tenemos el cuerpo de la página:


<body>
<h1>Ejemplo de descripción extensa de una imagen: accesible con lector de pantalla y sin imágenes activas</h1>
<p>Olga Carreras. Ejemplo del artículo <a href="http://olgacarreras.blogspot.com">"Descripción extensa de una imagen: accesible con lector de pantalla y sin imágenes activas"</a></p>

Después de lo que es el título de la página y el enlace al artículo tenemos ya la imagen:


<div>
<img src="razones_uso_tecnologia_accesible.gif" alt="Razones de uso de la tecnología accesible" id="image1" class="images" />
<p><a href="#descripcion" title="Mostrar la descripción de la imagen Razones de uso de la tecnología accesible" id="linkDescripcion" class="oculto">Ver descripción de la imagen</a></p>
</div>

La imagen es un gráfico, por tanto el atributo ALT no es suficiente, necesitamos una descripción detallada la imagen.

Imagen compleja de un gráfico con un enlace bajo la misma Mostrar descripción de la imagen

Os preguntaréis la razón por la cuál he incluido a continuación de la imagen un enlace "Ver descripción de la imagen".

Ese enlace no es necesario puesto que si la imagen no está cargada se verá la descripción detallada y los lectores de pantalla no van a tener problemas para acceder a dicha descripción.

Sin embargo, puede darse el caso de usuarios con vista cansada o que accedan desde un dispositivo móvil y el tamaño de letra de la imagen les resulte pequeño (y no sepan o no puedan hacer zoom de la imagen)

En ese caso les puede ser de utilidad pulsar el enlace para ver la descripción.

El enlace es un ancla al comienzo de la descripción, después veremos que mediante javascript no intrusivo se le ha asignado una función que permite mostrar/ocultar la descripción detallada y personalizar el texto y el TITLE del enlace en función de si se visualiza o no la descripción.


<div id="descripcion" class="visible">
<h2><abbr title="Flechas que señalan a la imagen">< <</abbr> Descripción de la imagen: Razones de uso de la tecnología accesible</h2>
<h3>Por entorno laboral</h3>
<ul>
<li>Con discapacidad severa: 4%</li>
<li>Con discapacidad moderada: 3%</li>
<li>Sin discapacidad: 6%</li>
</ul>
[el resto de la descripción larga, no la pongo aquí toda]
</div>

Como vemos, a continuación se incluye la descripción detallada correctamente maquetada. Esta descripción se oculta una vez cargada la página mediante javascript no intrusivo, por tanto estará disponible tanto sin javascript activo como sin CSS activas.

Con CSS activas, tanto si pulsas el enlace como si no se carga la imagen, la descripción se posiciona a la derecha de la imagen.

Imagen compleja de un gráfico, con un enlace bajo la misma Ocultar descripción de la imagen y la descripción detallada de la imagen al lado de la misma

Por otro lado veremos que la descripción extensa se está ocultando de forma accesible, así que los usuarios que usan un lector de pantalla (podéis probarlo con NVDA) escuchan la descripción a continuación de la imagen y el enlace.


<div class="pie">
<p>Probad a deshabilitar javascript, css o imágenes. Para ver sin imágenes cargadas o sin javascript activo acordaros de recargar la página una vez que los desactivéis.</p>
<p>Para facilitaros verla sin la imagen cargada os dejo una copia de la misma página pero donde se ha escrito mal el nombre de la imagen y por tanto no la encuentra: <a href="descripcion_imagenes_accesibles_sinimg.html">Probar esta página cuando no se encuentra la imagen.</a></p>
</div>
</body>
</html>

En el pie de la página os recuerdo que si deshabilitáis el javascript, las CSS o las imágenes mientras visualizáis la página deberéis recargarla. Para facilitaros comprobar que, efectivamente, cuando no se carga la imagen se muestra por defecto la descripción a su lado, os pongo un enlace a una copia de la página donde he escrito mal el nombre de la imagen para comprobar cómo se ve la página sin la imagen cargada.

Hueco donde debería estar la imagen con un texto en su interior. A su derecha la descripción detalla de la imagen

Como se observa, cuando la imagen no se ha podido mostrar, se ve en el hueco que ocuparía la imagen su ALT, y junto a la imagen aparece por defecto la descripción detallada. Ya no se muestra el enlace "Ver descripción de la imagen" porque no es necesario.

Me parece importante destacar que se ha separado:

  • el contenido: eso es únicamente lo que vemos en la página HTML
  • la presentación: no se especifica ningún estilo en la página, todos están recogidos en la CSS
  • el código javascript: no hay ningún evento, ninguna función, nada que haga sospechar que esta página incluye javascript (salvo la llamada al JS en el HEAD). Todo el javascript, incluso las funciones que se llaman en el onLoad de la página, están definidas de forma no intrusiva.

CSS

La CSS es CSS 2.1 válida. No pongo toda la CSS, sólo aquello que es relevante para el ejemplo.


.oculto{
position:absolute;
left:-999em;
}

Con esta clase general "oculto" oculto aquellos elementos que me interesa:

  • al cargarse la página compruebo si la imagen se ha cargado, en cuyo caso oculto por javascript la descripción. Sin javascript activo, por tanto, la descripción se verá.
  • el enlace "Ver descripción de la imagen" tiene esta clase "oculto" por defecto y una vez cargada la página lo hago visible. De esta manera, sin javascript activo, el enlace no se muestra puesto que ya no es necesario.

La forma de ocultar (mediante posicionamiento fuera de pantalla) permite que los lectores de pantalla sí puedan acceder al contenido oculto, algo que no ocurre con display:none. Más detalles en "Ocultar contenido sin comprometer la accesibilidad ni el posicionamiento de la página"

Otros estilos que me interesa comentar son los de la imagen:


.images{
width:auto;
height:auto;
}
.widthImage1{
width:393px;
height:363px;
}

Por defecto indico que el ancho de las imágenes sea "auto". No puedo poner por defecto el tamaño real de la imagen porque el script se basa en comprobar si el ancho de la imagen es diferente del ancho que debería tener, en cuyo caso es que la imagen no está cargada.

Sin embargo tengo una clase con el tamaño real de la imagen porque en Safari y Chrome, si la imagen no tiene especificado su ancho y alto real, no se muestra su atributo ALT. Así que veremos que en el script, tras comprobar el ancho de la imagen para saber si la imagen está cargada, le aplico la clase "widthImage1", y de este modo en Safari y Chrome vemos su ALT.

JS


function addLoadEvent(func) {
var oldonload = window.onload;
if (typeof window.onload != 'function') {
window.onload = func;
} else {
window.onload = function() {
if (oldonload) {
oldonload();
}
func();
}
}
}

La función "addLoadEvent" es la que nos permite llamar a funciones en el onLoad de la página de forma no intrusiva.

¿Qué funciones estamos llamando? Primero a addLoadEvent(noimage)


var Image1Width=393;
function noimage(){
var objImagen = document.getElementById('image1');
var objDescripcion = document.getElementById('descripcion');
var linkDescripcion = document.getElementById( 'linkDescripcion' );
objDescripcion.className = 'oculto';
linkDescripcion.className = 'visible';
if (objImagen.offsetWidth != Image1Width)
{
objDescripcion.className = 'visible';
objImagen.className = 'widthImage1';
linkDescripcion.className = 'oculto';
}
}

Lo primero que hago, como ya he comentado, es ocultar la descripción larga de la imagen y mostrar el enlace "Ver descripción de la imagen".

A continuación compruebo si el ancho de la imagen es diferente del ancho que debería tener, en cuyo caso es que la imagen no se ha cargado.

En la propuesta de S.Faulkner se comparaba si era igual a 1, pero esto no me funcionaba en todos los navegadores. Por eso lo comparo con el ancho real "393". Es más "sucio" porque no me sirve para todas las imágenes como el compararlo con 1, sino que he de personalizarlo para cada una, pero funciona en todos los navegadores.

Nota: Faulkner lo comparaba con 1 porque no le ponía ALT a las imágenes. Entonces el hueco de la imagen no se adaptaba al ALT y por eso su ancho era 1 cuando no estaba cargada. Como se debe poner ALT tenemos que hacerlo comparándolo con su ancho real. Por otro lado, indicar el ancho y alto de la imagen ayuda a las personas que ven la página sin la imagen cargada a hacerse una idea del tamaño que esta tenía.

En el caso de que la imagen no se haya cargado muestro la descripción, cambio la clase de la CSS aplicada a la imagen (ya he explicado en la CSS que es para que en Chrome y Safari se vea el atributo ALT dentro de la zona de la imagen) y oculto el enlace "Ver descripción de la imagen" porque el enlace ya no es necesario.

La otra función que llamo en el onLoad de la página es la siguiente:


addLoadEvent (function(){
var linkDescripcion = document.getElementById( 'linkDescripcion' );
var objDescripcion = document.getElementById('descripcion');
linkDescripcion.onclick = function(){
if (objDescripcion.className == 'oculto')
{
objDescripcion.className = 'visible';
linkDescripcion.innerHTML="Ocultar descripción de la imagen";
linkDescripcion.title="Ocultar la descripción de la imagen Razones de uso de la tecnología accesible"
}else{
objDescripcion.className = 'oculto';
linkDescripcion.innerHTML="Ver descripción de la imagen";
linkDescripcion.title="Mostrar la descripción de la imagen Razones de uso de la tecnología accesible"
}
}
}
)

Esta función añade al evento "onClick" del enlace "Ver descripción de la imagen" una función que:

  • Si la descripción de la imagen está oculta, al pulsar el enlace la mostraremos. Y a continuación cambiaremos el texto del enlace por su nueva acción "Ocultar descripción de la imagen" y cambiaremos también el TITLE de la imagen para que esté acorde con su nueva función de ocultar la descripción.
  • Si la descripción de la imagen está visible, al pulsar el enlace la ocultaremos. Y a continuación cambiaremos en texto del enlace por su nueva acción "Ver descripción de la imagen" y cambiaremos también el TITLE de la imagen para que esté acorde con su nueva función de mostrar la descripción.

Algo que no he hecho para que se viera más claro lo que hacían las funciones y que se debería hacer para separar el código javascript del texto concreto que queremos personalizar, sería definir esos literales en un fichero diferente de variables para que nos sean más sencillos de modificar. Así en el código sólo incluiriamos variables y no texto concreto.


Os animo ha que dejéis en los comentarios cualquier sugerencia de mejora o problema que detectéis.



Artículos relacionados:

martes, 5 de mayo de 2009

Técnicas WCAG 2.0 para 10 dudas habituales sobre accesibilidad

El contenido de este artículo se ha movido a la recopilación Respuesta a 25 dudas habituales sobre accesibilidad web

viernes, 27 de marzo de 2009

AJAX accesible IV: Técnicas ARIA de las WCAG 2.0

Las WCAG 2.0 (Web Content Accessibility Guidelines 2.0) incluyen cuatro técnicas (Techniques for WCAG 2.0: Techniques and Failures for Web Content Accessibility Guidelines 2.0) específicas relacionadas con WAI- ARIA.

ARIA1: Using Accessible Rich Internet Application describedby property to provide a descriptive, programmatically determined label



The purpose of this technique is to demonstrate how to use the Accessible Rich Internet Application (ARIA) descibedby property to provide descriptive information about a user interface control that can be programmatically determined by user agents.

ARIA techniques provide the ability to add programmatically determined information to an element which can provide additional information about the element. The user agent can provide this additional information to assistive technology for presentation to the user.



Ejemplo:

<p>The link in the next paragraph has been updated with the Accessible Rich Internet Applications describedby property to provide more information about the link</p>

<p><span id="icebergInfo">Alaskan storm cracks iceberg in Antarctica. </span>

A bad storm in Alaska last October generated an ocean swell that broke apart a giant iceberg near Antarctica six days later, U.S. researchers reported on Monday.

<a href="http://www.sciencemag.com/iceberg.html" id="iceberg" waistate:describedby="icebergInfo">More Info...</a>.
</p>


ARIA2: Identifying required fields with the "required" property



The objective of this technique is to indicate that the completion of a user input field is mandatory in a programmatically determinable way. The WAI-ARIA required state indicates that user input is required before submission. The "required" state can have values of "true" or "false". For example, if a user must fill in an address field, then "required" is set to true.

Note: The fact that the element is required is often visually presented (such as a sign or symbol after the control). Using the "required" property makes it much easier for user agents to pass on this important information to the user in a user agent-specific manner.



Ejemplo:

<label for="test">Test (required)</label>
<input name="test" id="test" aaa:required="true" />


ARIA3: Identifying valid range information with the "valuemin" and "valuemax" properties



The objective of this technique is to provide information about the allowable range of an entry field in a programmatically determinable way. The WAI-ARIA valuemin and valuemax states provide the minimum and maximum (respectively) values that may be provided by the user. User agents will not permit users to enter values outside that range, or will generate a validation error if users do so.



Ejemplo:

<form action="http://example.com/submit">

<p><label for="test">Enter a date in 2007:</label>
<input name="test" id="test" aaa:valuemin="2007-01-01" aaa:valuemax="2007-12-31" aaa:datatype="xsd:date" />
</p>

<p><input type="submit" value="Submit" /></p>

</form>


ARIA4: Using Accessible Rich Internet Applications to programmatically identify form fields as required



La manera de implementar la técnica ARIA2 de forma dinámica se ejemplifica así:

Ejemplo:

<head>
<script type="text/javascript">
//<![CDATA[

// array or ids on the required fields on this page
var requiredIds = new Array( "firstName", "lastName");

// function that is run after the page has loaded
to set the required role on each of the
//elements in requiredIds array of id values

function setRequired(){
if (requiredIds){
var field;
for (var i = 0; i< requiredIds.length; i++){
field = document.getElementById(requiredIds[i]);
setAttrNS(field, "required", "true");
}
}
}

// method to set the attribute values based on the capability
of the browser.
// Use setAttributeNS if it is available,
// otherwise append a namespace indicator string to the
attribute and set its value.

function setAttrNS(elemObj, theAttr, theValue){
if (typeof document.documentElement.setAttributeNS
!= 'undefined') {
elemObj.setAttributeNS
("http://www.w3.org/2005/07/aaa", theAttr, theValue);
}else{
elemObj.setAttribute("aaa:" + theAttr, theValue);
}
}
window.onload=setRequired;
//]]>
</script>
</head>
<body>
<p>Please enter the following data.
Required fields have been programmatically identified
as required and marked with an asterisk (*) following
the field label.</p>
<form action="submit.php">
<p>
<label for="firstName">First Name *: </label>
<input type="text" name="firstName"
id="firstName" value="" />
<label for="lastName">Last Name *: </label>
<input type="text" name="lastName"
id="lastName" value="" />
</p>
</form>
</body>

Artículos relacionados

lunes, 22 de octubre de 2007

HIJAX

Artículos relacionados
[14-11-13] Live Regions y WAI-ARIA. Cómo mejorar la accesibilidad de contenidos que se actualizan automáticamente
[07-09-07] WAI-ARIA. Introducción, referencias, ejemplos, herramientas
[26-05-07] AJAX accesible



El otro día me preguntaban qué era Hijax, si era la evolución de AJAX, o algo nuevo, o ¡qué era!, muy preocupado por si se estaba perdiendo algo referente a AJAX. No me voy a extender mucho, ya habló de Hijax Fran Tarifa el otro día en Hijax: ¿Ajax accesible? (aunque no estoy de acuerdo con sus conclusiones)

Hijax es una metodología a seguir a la hora de realizar aplicaciones AJAX, que consiste en aplicar a estos desarrollos la estrategia que se conoce como "mejora progresiva", propia del diseño web, para que dichos desarrollos AJAX sean accesibles.

Jeremy Keith ya hablaba de aplicar la "mejora progresiva" a AJAX en marzo de 2005, antes de que se le ocurriera bautizar a esta metodología como Hijax, en su artículo Progressive enhancement with Ajax.

Parece que esto de Hijax es nuevo, pero Jeremy no se inventó el término ayer, sino el 1 de enero de 2006 a las 7:40, en el artículo Hijax. Supongo que esa Nochevieja fue sonada para sentarse a primera hora a hablar de Hijax [bromilla].

El caso es que últimamente se oye hablar mucho de Hijax, como algo nuevo, lo cual se debe en parte a sus ponencias como "Bulletproof AJAX (Ajax a pruebas de balas)" y en parte a ese curioso boca a boca exponencial (buzz) propio de Internet.

Esto de bautizar a todo con un nombre gancho es muy habitual (AJAX, Web 2.0, etc.) y muy eficaz para estar en boca de todos (o en blog de todos, como tendríamos que decir hoy en día), pero bueno, no está mal si eso ayuda a difundir buenas prácticas. Hay que reconocerle a Jeremy Keith que lleva ya años abogando por hacer las cosas bien, es simplemente que me hace gracia como de repente todo el mundo habla de algo cotidiano y habitual como si fuera muy novedoso por tener un nombre vistoso. Él mismo ironizaba:

I’ve even got a nice shiny buzzword for this technique: Hijax. (Hijax, Jeremy Keith) [1]


Pero dime, ¿en qué consiste eso de Hijax? Pues me temo que te va a decepcionar un poco, porque no es más que aplicar aquello que venimos recomendando una y otra vez: JavaScript no intrusivo a la hora de plantear alternativas accesibles.



El resumen es el siguiente:

1. Haz que la aplicación funcione sin AJAX mediante peticiones normales al servidor que recarguen la página.

2. Ahora añade JavaScript no intrusivo para incluir el código AJAX, de este modo, si el user-agent no admite JavaScript o está desactivado, la aplicación seguirá funcionando correctamente, puesto que el código es invisible y no afecta al funcionamiento de la página.

Según dice Jeremy, la clave es: Plan for Ajax from the start. Implement Ajax at the end.



Nada nuevo, pero si llamarle Hijax ayuda a difundir las buenas prácticas de la programación AJAX, pues nada: viva Hijax! todos a aplicar Hijax! es decir, a seguir haciendo las cosas bien.




[1]

There seems to be an inherent paradox in saying that you need to think about your server-side architecture but you should just be building old-fashioned page by page submissions before hijacking them with Ajax (Hijax, Jeremy Keith)


Hijax, vendría del término "hijack", secuestrar.



Artículos relacionados
[14-11-13] Live Regions y WAI-ARIA. Cómo mejorar la accesibilidad de contenidos que se actualizan automáticamente
[26-05-07] AJAX accesible I
[07-09-07] AJAX accesible II: WAI-ARIA
[27-03-09] AJAX accesible IV: Técnicas ARIA de las WCAG 2.0