Progettazione Web - JavaScript nel browser
Cerca per parola chiave 0/0

JavaScript nel browser

DOM, eventi, form, chiamate asincrone e Canvas

Nel modulo precedente abbiamo imparato il linguaggio; ora lo usiamo dove è nato, dentro la pagina. Negli esempi ci sono tre schede, HTML, CSS e JS: dopo una modifica premi Esegui (o Ctrl+Invio) per ricaricare l'anteprima. I messaggi di console.log e gli errori compaiono nella console sotto l'anteprima.

Il DOM

La pagina come albero di oggetti

Quando il browser legge l'HTML costruisce in memoria il DOM (Document Object Model): un albero in cui ogni elemento, ogni pezzo di testo e ogni commento è un nodo, cioè un oggetto con proprietà e metodi. JavaScript non tocca il file HTML: lavora su questo albero, e ogni modifica all'albero si vede subito sullo schermo.

<html> <head>…</head> <body> <h1>Menu</h1> <p>Oggi <em>pizza</em></p> </body> </html> document html head body h1 p "Menu" "Oggi " em "pizza" nodo elemento nodo di testo
Il punto d'ingresso è l'oggetto document; da lì si raggiunge ogni nodo.

I tipi di nodo più importanti sono gli elementi (i tag) e i nodi di testo. Anche gli spazi e gli a capo tra un tag e l'altro diventano nodi di testo: per questo si preferiscono le proprietà che considerano solo gli elementi, come children invece di childNodes.

Selezionare gli elementi

MetodoRestituisce
document.getElementById('menu')l'elemento con quell'id, oppure null
document.querySelector('nav a.attivo')il primo elemento che corrisponde a un selettore CSS, oppure null
document.querySelectorAll('li')tutti gli elementi corrispondenti, in una NodeList statica (una «fotografia»)
document.getElementsByClassName('x')
document.getElementsByTagName('p')
una HTMLCollection «viva», che si aggiorna da sola se la pagina cambia

querySelector e querySelectorAll accettano qualsiasi selettore CSS e sono i più usati. Si possono chiamare anche su un elemento, per cercare solo tra i suoi discendenti: menu.querySelectorAll('a'). Una NodeList si scorre con forEach o for…of; per usare map o filter la si converte in array con [...lista].

Leggere e cambiare gli elementi

Ogni elemento è un oggetto con molte proprietà. Le più usate:

textContent

Il testo contenuto, senza tag. Assegnandolo si sostituisce tutto il contenuto con testo semplice.

innerHTML

Il contenuto come codice HTML. Assegnandolo, il browser interpreta i tag. Potente e pericoloso.

classList

add, remove, toggle, contains, replace sulle classi. Il modo giusto per cambiare l'aspetto.

style

Lo stile in linea. I nomi delle proprietà CSS diventano camelCase: style.backgroundColor.

id, src, href, value…

Gli attributi standard sono anche proprietà. Per gli altri: getAttribute, setAttribute, removeAttribute.

dataset

Gli attributi data-*: data-prezzo diventa dataset.prezzo, data-id-prodotto diventa dataset.idProdotto. Sempre stringhe.

Meglio cambiare classi che stili: lo stile resta nel CSS, dove si gestiscono anche stati come :hover, transizioni e media query, e il JavaScript si limita a dire «ora questo elemento è attivo».

La differenza tra textContent e innerHTML è una questione di sicurezza. Se il testo viene dall'utente e lo si inserisce con innerHTML, qualsiasi tag che contiene diventa parte della pagina, compresi quelli che eseguono codice. È il principio degli attacchi XSS (cross-site scripting), che rivedremo nel modulo sulla sicurezza.

Creare, spostare e rimuovere

MetodoChe cosa fa
document.createElement('li')crea un elemento, non ancora nella pagina
padre.append(a, b, 'testo') / prepend(…)inserisce in fondo / in cima ai figli (elementi o stringhe)
el.before(…) / el.after(…)inserisce prima / dopo l'elemento, come fratello
padre.appendChild(figlio), padre.insertBefore(nuovo, riferimento)le versioni storiche, ancora diffusissime
el.remove() / padre.removeChild(figlio)toglie l'elemento dalla pagina
el.replaceWith(nuovo), el.cloneNode(true)sostituisce; copia (con true anche i discendenti)

Per muoversi nell'albero partendo da un elemento ci sono le «relazioni di parentela»: parentElement, children, firstElementChild, lastElementChild, previousElementSibling, nextElementSibling. Il metodo closest(selettore) risale gli antenati fino al primo che corrisponde: utilissimo negli eventi.

Quando il DOM è pronto

Uno script può usare solo gli elementi che il browser ha già letto. Uno script nel head senza defer viene eseguito prima che il body esista, e ogni querySelector restituisce null. Le soluzioni:

  • mettere lo script in fondo al body;
  • usare <script defer> (o type="module"), la soluzione moderna;
  • aspettare l'evento DOMContentLoaded, lanciato quando l'HTML è stato letto tutto. L'evento load di window arriva più tardi, quando sono state caricate anche immagini e fogli di stile.

Gli eventi

addEventListener

Un evento è qualcosa che succede: un clic, un tasto premuto, un campo modificato, la pagina caricata. Si reagisce registrando una funzione, il gestore (event handler o listener). Ci sono tre modi:

L'oggetto evento

Il gestore riceve come argomento un oggetto evento con i dettagli di ciò che è successo:

  • type: il nome dell'evento ('click', 'keydown'…);
  • target: l'elemento su cui è avvenuto davvero; currentTarget: quello a cui è collegato il gestore che sta rispondendo;
  • per il mouse clientX, clientY (rispetto alla finestra) e offsetX, offsetY (rispetto all'elemento); per la tastiera key (il carattere, come 'a' o 'Enter') e code (il tasto fisico); per entrambi shiftKey, ctrlKey, altKey;
  • preventDefault() annulla il comportamento predefinito del browser: l'invio di un form, il salto di un link, lo scorrimento con la barra spaziatrice.

Cattura e propagazione

Un clic su un pulsante dentro un div è anche un clic sul div, sul body e su tutto il documento. Per questo un evento attraversa l'albero in tre fasi:

  1. cattura: scende da document verso l'elemento cliccato;
  2. bersaglio: raggiunge l'elemento (target);
  3. propagazione o bubbling: risale, come una bolla, fino a document.

I gestori normali rispondono nella fase di propagazione; con addEventListener(tipo, f, true) rispondono nella fase di cattura. e.stopPropagation() ferma il viaggio dell'evento. Pochi eventi non risalgono, come focus e blur.

document body div#padre button (target) 1. cattura addEventListener(…, true) 3. propagazione il comportamento normale
Un evento scende verso il bersaglio e poi risale: ogni antenato ha due occasioni per intercettarlo.

La delega degli eventi

La propagazione permette un trucco molto usato: invece di collegare un gestore a ciascuno di cento pulsanti, se ne collega uno solo al contenitore e si guarda e.target per capire chi è stato cliccato. Si chiama delega (event delegation): usa meno memoria e funziona anche per gli elementi aggiunti dopo, come nella lista di cose da fare vista sopra.

I tipi di evento

FamigliaEventi
mouse e puntatoreclick, dblclick, mousedown, mouseup, mousemove, mouseover/mouseout (risalgono), mouseenter/mouseleave (non risalgono), contextmenu, wheel; i pointer… unificano mouse, penna e dita
tastierakeydown (anche ripetuto se si tiene premuto), keyup
forminput (a ogni modifica), change (a modifica conclusa), focus, blur, submit, reset, select (testo selezionato)
documento e finestraDOMContentLoaded, load, resize, scroll, beforeunload, visibilitychange
mediaplay, pause, ended, timeupdate, volumechange

Form e validazione

Leggere i campi

  • campo.value contiene il valore, sempre come stringa: va convertito con Number() prima dei calcoli. Per i campi numerici c'è anche valueAsNumber;
  • per caselle e pulsanti di scelta si legge checked;
  • per un select, value è il valore dell'opzione scelta e selectedOptions la lista delle opzioni selezionate (più di una se c'è multiple);
  • i campi di un form sono raggiungibili per name: form.elements.qta o semplicemente form.qta;
  • new FormData(form) raccoglie tutte le coppie nome-valore, come le invierebbe il browser.

Validare: perché, dove, come

Validare significa controllare che i dati inseriti abbiano senso prima di usarli. Va fatto in due posti, per due motivi diversi:

Nel browser

Per l'utente: lo avvisa subito, senza attendere il server, e gli evita di perdere ciò che ha scritto. Ma si può aggirare: basta disattivare JavaScript o mandare la richiesta con un altro programma.

Sul server

Per la sicurezza e la correttezza dei dati: è l'unico controllo che l'utente non può saltare. È obbligatoria, sempre, anche se c'è già quella nel browser.

I controlli tipici sono di cinque tipi: obbligatorietà (il campo non è vuoto), formato (un'email, un CAP, una data), confronto (le due password coincidono, la data di ritorno viene dopo quella di andata), intervallo (un'età tra 18 e 120) e controlli personalizzati (il nome utente non è già preso). Quando qualcosa non va, il messaggio deve dire che cosa è sbagliato e come correggerlo, comparire vicino al campo, e non affidarsi solo al colore rosso, invisibile a chi non distingue i colori.

La validazione di HTML5 e il CSS

Molti controlli si ottengono senza scrivere JavaScript, con gli attributi visti nel modulo HTML: required, type="email", min, max, minlength, maxlength, pattern (un'espressione regolare). Il browser blocca l'invio e mostra un fumetto sul primo campo sbagliato. L'attributo novalidate sul form disattiva questi controlli, se si preferisce gestire tutto con JavaScript.

Il CSS può dare un aspetto ai campi con le pseudo-classi :valid e :invalid, che però si attivano subito, anche sui campi ancora vuoti. Le più recenti :user-valid e :user-invalid aspettano che l'utente abbia interagito con il campo. Per le regole che l'HTML non sa esprimere c'è setCustomValidity(messaggio): con un messaggio il campo diventa non valido, con la stringa vuota torna valido.

Le espressioni regolari

Un'espressione regolare (regex) è un modello che descrive un insieme di stringhe: «cinque cifre», «lettere, una chiocciola, altre lettere, un punto». Si usa per verificare un formato, cercare e sostituire. In JavaScript si scrive tra due barre, /^\d{5}$/, seguita da eventuali flag: i ignora maiuscole e minuscole, g trova tutte le occorrenze, m fa valere ^ e $ su ogni riga. La stessa sintassi vale in PHP, in MySQL (REGEXP) e nell'attributo pattern.

Un carattere normale corrisponde a se stesso. Alcuni caratteri, i metacaratteri, hanno un significato speciale: . ^ $ * + ? ( ) [ ] { } | \. Per usarli come caratteri normali si fanno precedere da \: \. è un punto vero.

SimboloSignificatoEsempio
.qualsiasi carattere tranne l'a capoc.sa → casa, cosa, c3sa
^ $inizio e fine della stringa^ciao$ → solo «ciao»
[abc] [a-z]uno dei caratteri elencati, o un intervallo[A-Z][a-z]+ → Pisa
[^0-9]un carattere non elencatotutto tranne le cifre
\d \w \scifra, carattere di parola (lettere, cifre, _), spazio\d\d:\d\d → 10:30
\D \W \Sil contrario dei precedenti
\bconfine di parola\bre\b trova «re», non «mare»
* + ?0 o più, 1 o più, 0 o 1 voltecolou?r → color, colour
{n} {n,} {n,m}esattamente n, almeno n, da n a m volte\d{2,4}
a|balternativagatto|cane
( )gruppo: si può ripetere e se ne recupera il contenuto(ah)+ → ahahah
(?= )lookahead: «più avanti deve esserci…», senza consumare caratteri(?=.*\d) → contiene una cifra

I quantificatori sono avidi: prendono più caratteri possibile. Aggiungendo ? (*?, +?) diventano pigri e si fermano appena possono. Prova tutto qui sotto: scegli un esempio, poi modifica l'espressione o le stringhe.

In JavaScript le espressioni regolari si usano con questi metodi:

Validazione con JavaScript

Quando servono messaggi personalizzati e controlli più ricchi, si disattiva la validazione del browser con novalidate e si controlla tutto nell'evento submit: se qualcosa non va si chiama preventDefault() e si mostrano gli errori accanto ai campi. Gli attributi aria-invalid e aria-describedby fanno leggere l'errore anche ai lettori di schermo.

Programmazione asincrona

Timer

setTimeout(f, ms) esegue f una volta dopo almeno ms millisecondi; setInterval(f, ms) la esegue ogni ms millisecondi. Entrambe restituiscono un numero che serve a fermarle con clearTimeout e clearInterval. Il codice non si ferma ad aspettare: prosegue subito con la riga successiva.

Un solo thread e il ciclo degli eventi

JavaScript nel browser esegue una cosa alla volta, su un solo filo di esecuzione (thread). Le operazioni lente (un timer, una richiesta di rete, l'attesa di un clic) non bloccano tutto perché le gestisce il browser, fuori da JavaScript, nelle cosiddette Web API. Quando un'operazione finisce, la sua callback viene messa in una coda. Il ciclo degli eventi (event loop) prende la prossima callback dalla coda solo quando lo stack delle chiamate è vuoto, cioè quando il codice in corso è terminato.

Stack delle chiamate main() calcola() Web API del browser timer, rete (fetch), eventi del DOM… microtask callback delle Promise (then, await): hanno la precedenza task callback di setTimeout, clic, caricamenti… avvia a lavoro finito, la callback va in coda ciclo degli eventi: a stack vuoto, prima tutti i microtask, poi un task
Una callback non interrompe mai il codice in corso: aspetta il suo turno in coda.

AJAX: dati senza ricaricare la pagina

Nel Web classico ogni azione dell'utente (un link, un form) produce una richiesta al server che risponde con una pagina intera, e il browser la ridisegna da capo. Con AJAX (Asynchronous JavaScript and XML) è JavaScript a fare la richiesta, in sottofondo: il server risponde con i soli dati (oggi quasi sempre in JSON, nonostante la X del nome) e lo script aggiorna solo la parte di pagina che serve. L'utente intanto può continuare a usare la pagina.

Modello classico AJAX browser tutta la pagina server GET pagina.php HTML completo la pagina sparisce e si ridisegna browser solo qui fetch() server GET api.php?q=… dati JSON la pagina resta lì: cambia solo un pezzo
Con AJAX viaggiano meno dati e l'interfaccia non si interrompe.

Lo strumento storico per AJAX è l'oggetto XMLHttpRequest, basato su callback. Lo incontrerai nel codice meno recente:

Dalle callback alle Promise

Con le sole callback, operazioni asincrone in sequenza finiscono annidate una dentro l'altra: «carica l'utente, poi i suoi ordini, poi i dettagli del primo ordine». Il codice va sempre più a destra e la gestione degli errori va ripetuta a ogni livello. È il cosiddetto callback hell:

Una Promise («promessa») è un oggetto che rappresenta un risultato che arriverà. È in uno di tre stati: pending (in attesa), poi fulfilled (mantenuta, con un valore) oppure rejected (rifiutata, con un errore). Una volta deciso, lo stato non cambia più. Con .then(f) si dice che cosa fare con il valore, con .catch(f) che cosa fare in caso di errore, con .finally(f) che cosa fare comunque. Siccome then restituisce a sua volta una Promise, le operazioni si concatenano in verticale, con un solo catch in fondo.

fetch

fetch(indirizzo) è il modo moderno di fare una richiesta HTTP: restituisce una Promise che si risolve con un oggetto Response. Il corpo della risposta si legge con un'altra Promise: response.json() per il JSON, response.text() per il testo. Attenzione: fetch viene rifiutata solo per errori di rete; una risposta 404 o 500 è comunque una risposta, quindi va controllato response.ok (vero per i codici da 200 a 299).

Per inviare dati si passa un secondo argomento con metodo, intestazioni e corpo. Con un form il corpo può essere direttamente un FormData; per mandare JSON lo si converte in testo e lo si dichiara nell'intestazione Content-Type:

Richieste verso altri siti. Per sicurezza il browser applica la same-origin policy: una pagina può leggere le risposte del proprio sito, ma quelle di un altro dominio solo se quel server lo permette esplicitamente con l'intestazione Access-Control-Allow-Origin. È il meccanismo CORS. Molte API pubbliche lo fanno; quando non lo fanno, si passa dal proprio server, come vedremo nel modulo sui web service.

async e await

Le parole chiave async e await permettono di scrivere codice asincrono che si legge come quello sincrono. Una funzione dichiarata async restituisce sempre una Promise; al suo interno, await promessa sospende la funzione (non la pagina!) finché la Promise non è risolta, e ne restituisce il valore. Gli errori si gestiscono con il normale try … catch.

await si può usare solo dentro funzioni async (oppure al livello più esterno di un modulo). Due attese di fila sono in sequenza: se le richieste sono indipendenti conviene farle partire insieme con await Promise.all([…]).

Disegnare con Canvas

L'elemento canvas e il contesto 2D

L'elemento <canvas> è una tela di pixel su cui si disegna con JavaScript: grafici, giochi, editor di immagini. Le sue dimensioni in pixel si danno con gli attributi width e height (il CSS lo ingrandirebbe o rimpicciolirebbe come un'immagine). Per disegnare si chiede il contesto con getContext('2d'): è l'oggetto con tutti i metodi di disegno. L'origine delle coordinate è nell'angolo in alto a sinistra e la y cresce verso il basso.

Canvas lavora «a pennellate»: una volta disegnata, una forma diventa pixel e non esiste più come oggetto. Per muoverla bisogna cancellare e ridisegnare. È la differenza con l'SVG, dove ogni forma resta un elemento del DOM.

Percorsi, archi e curve

Le forme diverse dal rettangolo si costruiscono come percorsi: beginPath() ne inizia uno, moveTo(x, y) sposta la penna senza tracciare, lineTo(x, y) traccia un segmento, arc(x, y, r, inizio, fine) un arco (angoli in radianti: un giro è 2 * Math.PI), quadraticCurveTo e bezierCurveTo le curve, closePath() chiude tornando al punto di partenza. Il percorso diventa visibile solo con stroke() (contorno) o fill() (riempimento).

Testo, immagini, gradienti e trasformazioni

  • testo: fillText(testo, x, y) e strokeText, con le proprietà font (come nel CSS), textAlign e textBaseline;
  • immagini: drawImage(img, x, y, larghezza, altezza), ma solo dopo che l'immagine è stata caricata, cioè nel suo evento load;
  • gradienti: createLinearGradient(x0, y0, x1, y1) o createRadialGradient, con i colori aggiunti da addColorStop(posizione, colore); il gradiente si usa come fillStyle. Con createPattern(img, 'repeat') si riempie con un'immagine ripetuta;
  • ombre: shadowColor, shadowBlur, shadowOffsetX, shadowOffsetY;
  • trasformazioni: translate, rotate, scale spostano, ruotano e scalano il sistema di coordinate per tutto ciò che si disegna dopo. save() salva lo stato (colori, spessori, trasformazioni) e restore() lo ripristina.

Animazione e tastiera

Un'animazione è una sequenza di fotogrammi: a ogni passo si aggiorna lo stato (le posizioni), si cancella la tela e si ridisegna tutto. Il ritmo si dà con requestAnimationFrame(f), che chiede al browser di chiamare f prima del prossimo aggiornamento dello schermo, di solito 60 volte al secondo, e si mette in pausa da solo se la scheda non è visibile. La tastiera si legge con keydown e keyup, ricordando quali tasti sono premuti.

Altre API del browser

Web Storage

Il browser offre a ogni sito un piccolo archivio di coppie chiave-valore, in due versioni:

  • localStorage: i dati restano anche chiudendo il browser, finché il sito o l'utente non li cancellano;
  • sessionStorage: i dati durano finché resta aperta la scheda.

I metodi sono gli stessi: setItem(chiave, valore), getItem(chiave) (restituisce null se manca), removeItem(chiave), clear(). I valori sono solo stringhe: per salvare oggetti e array si passa da JSON.stringify e JSON.parse. Ogni sito vede solo il proprio archivio, di qualche megabyte. A differenza dei cookie questi dati non vengono inviati al server: servono a ricordare preferenze, bozze, dati già scaricati. Non metterci mai informazioni riservate: qualsiasi script della pagina può leggerle.

Geolocalizzazione, sintesi vocale e librerie

Il browser espone molte altre interfacce. Quelle che riguardano la privacy, come la posizione, la fotocamera o il microfono, funzionano solo su pagine HTTPS (o localhost) e chiedono il permesso all'utente.

Per compiti complessi si usano librerie già pronte, caricate con un tag <script> da una CDN: Leaflet per le mappe, Chart.js o Plotly per i grafici. Molti servizi (mappe, meteo, pagamenti) offrono API web che si interrogano con fetch, spesso con una chiave personale da richiedere al fornitore.

Accessibilità dei componenti dinamici

Le WCAG 2.2 in breve

Nel modulo HTML abbiamo visto le regole d'oro dell'accessibilità. Con JavaScript si costruiscono menu, schede, finestre di dialogo e messaggi che compaiono: qui è facilissimo escludere chi usa la tastiera o un lettore di schermo. Il riferimento sono le WCAG 2.2 (Web Content Accessibility Guidelines) del W3C, organizzate su quattro principi, riassunti nella sigla POUR:

Percepibile

L'informazione deve arrivare a tutti i sensi disponibili: testo alternativo, sottotitoli, contrasto sufficiente, niente informazioni affidate al solo colore.

Utilizzabile

Tutto si deve poter usare con la tastiera, con il fuoco visibile, senza limiti di tempo stretti e senza lampeggiamenti pericolosi.

Comprensibile

Testi chiari, comportamento prevedibile, errori dei moduli spiegati e facili da correggere.

Robusto

Il codice deve essere interpretato correttamente da browser e tecnologie assistive: HTML valido, nome, ruolo e stato di ogni controllo.

Sotto i principi ci sono 13 linee guida e, per ciascuna, i criteri di successo: affermazioni verificabili, classificate in tre livelli di conformità, A (il minimo), AA e AAA (il massimo). In Europa la norma EN 301 549 richiede il livello AA, obbligatorio per i siti pubblici e, dal 2025, per molti servizi privati.

CriterioLivelloIn pratica
1.1.1 Contenuti non testualiAogni immagine significativa ha un testo alternativo
1.4.3 Contrasto minimoAAtesto con rapporto di contrasto di almeno 4,5:1 (3:1 se grande)
2.1.1 TastieraAogni funzione si usa da tastiera
2.4.7 Fuoco visibileAAsi vede sempre quale elemento ha il fuoco
2.5.8 Dimensione dei bersagliAApulsanti e link di almeno 24 × 24 pixel (novità della 2.2)
3.3.1 Identificazione degli erroriAil campo sbagliato è indicato e l'errore descritto a parole
4.1.2 Nome, ruolo, valoreAogni controllo ha un nome accessibile, un ruolo e uno stato leggibili

ARIA: ruoli, proprietà e stati

Il lettore di schermo non legge i pixel: legge l'albero di accessibilità, una versione del DOM in cui ogni elemento ha un ruolo (pulsante, link, titolo…), un nome e degli stati. Gli elementi HTML nativi li hanno già. Quando si costruisce un componente con div e JavaScript, gli attributi ARIA servono a completare queste informazioni:

  • ruoli (role="…"): di widget (button, checkbox, tab, dialog, menu), di struttura (list, heading), di riferimento (banner, navigation, main, che corrispondono a header, nav, main) e di regione dinamica (alert, status);
  • proprietà, che cambiano di rado: aria-label (un nome dato direttamente), aria-labelledby (il nome è il testo di un altro elemento), aria-describedby (una descrizione aggiuntiva), aria-required, aria-controls, aria-current="page" (la voce di menu della pagina attuale);
  • stati, che JavaScript aggiorna: aria-expanded, aria-pressed, aria-checked, aria-selected, aria-disabled, aria-invalid, aria-hidden.

ARIA cambia solo ciò che viene annunciato, non il comportamento: role="button" su un div non lo rende cliccabile da tastiera. Le regole da ricordare:

  1. se esiste un elemento HTML nativo che fa quella cosa, usa quello;
  2. non cambiare il significato degli elementi nativi (niente <h2 role="button">);
  3. ogni controllo ARIA deve funzionare da tastiera;
  4. non mettere aria-hidden="true" o role="presentation" su elementi che ricevono il fuoco;
  5. ogni elemento interattivo deve avere un nome accessibile.

Per la tastiera c'è l'attributo tabindex: 0 inserisce un elemento nell'ordine di tabulazione naturale, -1 lo rende raggiungibile solo con focus() da JavaScript, mentre i valori positivi stravolgono l'ordine e vanno evitati. L'ordine di tabulazione segue l'ordine del codice: per questo l'HTML deve essere scritto nell'ordine logico di lettura, anche se il CSS poi sposta visivamente le cose.

Componenti accessibili all'opera

Tre schemi frequenti: un pulsante che apre un menu (aria-expanded), un pulsante «finto» costruito con un div (e tutto il lavoro necessario per renderlo usabile) e una regione dinamica (live region), il cui contenuto viene letto ad alta voce quando cambia. Con aria-live="polite" (o role="status") il lettore aspetta di finire la frase in corso; con assertive (o role="alert") interrompe, e va riservato agli errori urgenti.

Come verificare

  • la tastiera: il test più semplice e rivelatore. Metti via il mouse e prova a usare tutta la pagina con Tab, Maiusc+Tab, Invio, la barra spaziatrice e le frecce;
  • strumenti automatici: l'estensione WAVE, il pannello Lighthouse di Chrome, l'ispettore di accessibilità di Firefox, che mostra l'albero di accessibilità. Trovano circa un terzo dei problemi: il resto richiede una persona;
  • un lettore di schermo: NVDA (gratuito, Windows), VoiceOver (integrato in macOS e iOS, si attiva con Cmd+F5), TalkBack su Android.

Verifica