Una panoramica di base su come creare un componente reattivo, adattivo, accessibile e a selezione multipla per ordinare e filtrare le esperienze utente.
In questo post voglio condividere alcune idee su come creare un componente di selezione multipla. Prova la demo.
Se preferisci i video, ecco una versione di questo post su YouTube:
Panoramica
Spesso agli utenti vengono presentati degli articoli, a volte molti, e in questi casi può essere una buona idea fornire un modo per ridurre l'elenco in modo da evitare un sovraffollamento di opzioni. Questo post del blog spiega come filtrare l'interfaccia utente per ridurre le scelte. Per farlo, presenta gli attributi degli articoli che gli utenti possono selezionare o deselezionare, riducendo i risultati e quindi il sovraccarico di scelta.
Interazioni
L'obiettivo è consentire l'esplorazione rapida delle opzioni di filtro per tutti gli utenti e i relativi tipi di input diversi. Verrà fornito con una coppia di componenti adattabili e adattabili. Una barra laterale tradizionale di caselle di controllo per computer, tastiera
e screen reader e un <select
multiple>
per gli utenti che utilizzano dispositivi touch.
Questa decisione di utilizzare la selezione multipla integrata per il tocco e non per il desktop consente di risparmiare e creare lavoro, ma ritengo che offra esperienze appropriate con meno debito di codice rispetto alla creazione dell'intera esperienza adattabile in un unico componente.
Tocco
Il componente tocco consente di risparmiare spazio e migliora l'accuratezza dell'interazione utente su dispositivi mobili. Risparmia spazio comprimendo un'intera barra laterale di caselle di controllo in un'<select>
esperienza touch in overlay integrata. Migliora l'accuratezza dell'input mostrando
un'esperienza di overlay tocco di grandi dimensioni fornita dal sistema.
Tastiera e gamepad
Di seguito è riportata una dimostrazione di come utilizzare un <select multiple>
dalla tastiera.
Questa selezione multipla integrata non può essere personalizzata ed è disponibile solo in un layout compatto non adatto alla presentazione di molte opzioni. Capisci che non puoi vedere tutta la gamma di opzioni in quella piccola casella? Puoi modificarne le dimensioni, ma non comunque come una barra laterale di caselle di controllo.
Segni e linee
Entrambi i componenti saranno contenuti nello stesso elemento <form>
. I risultati di questo modulo, che siano caselle di controllo o selezione multipla, verranno osservati e utilizzati per filtrare la griglia, ma potrebbero anche essere inviati a un server.
<form>
</form>
Componente Caselle di controllo
I gruppi di caselle di controllo devono essere racchiusi in un elemento
<fieldset>
e devono avere un valore
<legend>
.
Quando l'HTML è strutturato in questo modo, gli screen reader e FormData comprendono automaticamente la relazione tra gli elementi.
<form>
<fieldset>
<legend>New</legend>
… checkboxes …
</fieldset>
</form>
Con il raggruppamento attivo, aggiungi un valore <label>
e <input type="checkbox">
per
ciascuno dei filtri. Ho scelto di racchiuderle in un <div>
in modo che la proprietà gap
CSS possa distanziarle in modo uniforme e mantenere l'allineamento quando le etichette diventano su più righe.
<form>
<fieldset>
<legend>New</legend>
<div>
<input type="checkbox" id="last 30 days" name="new" value="last 30 days">
<label for="last 30 days">Last 30 Days</label>
</div>
<div>
<input type="checkbox" id="last 6 months" name="new" value="last 6 months">
<label for="last 6 months">Last 6 Months</label>
</div>
</fieldset>
</form>
Componente <select multiple>
Una funzionalità raramente utilizzata dell'elemento <select>
è
multiple
.
Quando l'attributo viene utilizzato con un elemento <select>
, l'utente può scegliere più elementi dall'elenco. È come cambiare l'interazione da un elenco di opzioni a un elenco di caselle di controllo.
<form>
<select multiple="true" title="Filter results by category">
…
</select>
</form>
Per etichettare e creare gruppi all'interno di un <select>
, utilizza l'elemento <optgroup>
e assegnagli un attributo e un valore label
. Questo elemento e questo valore dell'attributo sono simili agli elementi <fieldset>
e <legend>
.
<form>
<select multiple="true" title="Filter results by category">
<optgroup label="New">
…
</optgroup>
</select>
</form>
Ora aggiungi gli elementi
<option>
per il filtro.
<form>
<select multiple="true" title="Filter results by category">
<optgroup label="New">
<option value="last 30 days">Last 30 Days</option>
<option value="last 6 months">Last 6 Months</option>
</optgroup>
</select>
</form>
Monitoraggio dell'input con contatori per fornire informazioni alle tecnologie per la disabilità
La tecnica del ruolo
di stato
viene utilizzata in questa esperienza utente per monitorare e gestire il conteggio dei
filtri per screen reader e altre tecnologie per la disabilità. Il video di YouTube
dimostra la funzionalità. L'integrazione inizia con il codice HTML e l'attributo role="status"
.
<div role="status" class="sr-only" id="applied-filters"></div>
Questo elemento leggerà ad alta voce le modifiche apportate ai contenuti. Possiamo aggiornare i contenuti con contatori CSS man mano che gli utenti interagiscono con le caselle di controllo. Per farlo, dobbiamo prima creare un contatore con un nome in un elemento padre degli elementi di input e stato.
aside {
counter-reset: filters;
}
Per impostazione predefinita, il conteggio sarà 0
, il che è ottimo, nessun valore è :checked
per impostazione predefinita in questa progettazione.
A questo punto, per incrementare il contatore appena creato, sceglieremo come target gli elementi secondari dell'elemento <aside>
che sono :checked
. Quando l'utente modifica lo stato degli input,
il contatore filters
viene conteggiato.
aside :checked {
counter-increment: filters;
}
Ora CSS è a conoscenza del conteggio generale dell'interfaccia utente della casella di controllo e dell'elemento status role
vuoto e in attesa di valori. Poiché il CSS gestisce il conteggio in memoria, la funzione counter()
consente di accedere al valore dai contenuti dell'elemento
pseudo:
aside #applied-filters::before {
content: counter(filters) " filters ";
}
L'HTML per l'elemento del ruolo di stato ora annuncerà "2 filtri" a uno screen reader. È un buon inizio, ma possiamo fare di meglio, ad esempio condividere il conteggio dei risultati aggiornati dai filtri. Eseguiremo questo lavoro da JavaScript, poiché non rientra nelle funzionalità dei contatori.
Eccitazione per i nidi
L'algoritmo dei contatori era molto bello con CSS nesting-1, dato che potevo inserire tutta la logica in un blocco. Per la lettura e l'aggiornamento è portatile e centralizzato.
aside {
counter-reset: filters;
& :checked {
counter-increment: filters;
}
& #applied-filters::before {
content: counter(filters) " filters ";
}
}
Layout
Questa sezione descrive i layout tra i due componenti. La maggior parte degli stili di layout riguarda il componente delle caselle di controllo per desktop.
Il modulo
Per ottimizzare la leggibilità e la scansione per gli utenti, al modulo viene assegnata una larghezza massima di 30 caratteri, impostando in sostanza una larghezza della riga ottica per ogni etichetta del filtro. Il modulo utilizza il layout a griglia e la proprietà gap
per distribuire gli elementi form.
form {
display: grid;
gap: 2ch;
max-inline-size: 30ch;
}
Elemento <select>
L'elenco di etichette e caselle di controllo occupa troppo spazio sui dispositivi mobili. Pertanto, il layout controlla il dispositivo di puntamento principale dell'utente per modificare l'esperienza per il tocco.
@media (pointer: coarse) {
select[multiple] {
display: block;
}
}
Un valore coarse
indica che l'utente non potrà interagire con lo schermo con un'elevata precisione con il suo dispositivo di input principale. Su
un dispositivo mobile, il valore del puntatore è spesso coarse
, poiché l'interazione principale
è il tocco. Su un computer, il valore del cursore è spesso fine
perché è comune avere un mouse o un altro dispositivo di input ad alta precisione collegato.
I fieldset
Lo stile e il layout predefiniti di un <fieldset>
con un <legend>
sono unici:
Normalmente, per spaziare gli elementi secondari, utilizzerei la proprietà gap
, ma il posizionamento unico di <legend>
rende difficile creare un insieme di elementi secondari con spaziatura uniforme. Anziché gap
, vengono utilizzati il selettore di fratelli adiacenti e margin-block-start
.
fieldset {
padding: 2ch;
& > div + div {
margin-block-start: 2ch;
}
}
In questo modo, lo spazio di <legend>
non viene modificato perché scegli come target solo i bambini <div>
.
L'etichetta e la casella di controllo del filtro
Poiché è un elemento secondario diretto di un <fieldset>
e rientra nella larghezza massima del 30ch
del modulo, il testo dell'etichetta potrebbe essere a capo se troppo lungo. L'aggregazione del testo va bene, mentre il disallineamento tra testo e casella di controllo no. Flexbox è l'ideale.
fieldset > div {
display: flex;
gap: 2ch;
align-items: baseline;
}
La griglia animata
L'animazione del layout è eseguita da Isotope. Un plug-in efficiente e potente per ordinare e filtrare interattivi.
JavaScript
Oltre ad aiutare a orchestrare una griglia animata e interattiva ordinata, JavaScript viene utilizzato per perfezionare un paio di aspetti.
Normalizzazione dell'input dell'utente
Questo design ha un unico modulo con due diversi modi per fornire input e non vengono serie lo stesso. Tuttavia, con un po' di JavaScript possiamo normalizzare i dati.
Ho scelto di allineare la struttura di dati dell'elemento <select>
alla struttura delle caselle di controllo raggruppate. A questo scopo, viene aggiunto un
input
listener di eventi all'elemento <select>
, a quel punto
selectedOptions
vengono mappati.
document.querySelector('select').addEventListener('input', event => {
// make selectedOptions iterable then reduce a new array object
let selectData = Array.from(event.target.selectedOptions).reduce((data, opt) => {
// parent optgroup label and option value are added to the reduce aggregator
data.push([opt.parentElement.label.toLowerCase(), opt.value])
return data
}, [])
})
Ora puoi inviare il modulo in tutta sicurezza oppure, nel caso di questa demo, puoi indicare a Isotope su cosa applicare i filtri.
Completamento dell'elemento "Status Role" in corso...
L'elemento conteggia e annuncia solo il numero di filtri in base all'interazione con le caselle di controllo, ma ho pensato che fosse una buona idea condividere anche il numero di risultati e assicurarmi che vengano conteggiate anche le scelte dell'elemento <select>
.
La scelta dell'elemento <select>
si riflette in counter()
Nella sezione di normalizzazione dei dati è già stato creato un ascoltatore all'input. Alla fine di questa funzione sono noti il numero di filtri scelti e il numero di risultati per questi filtri. I valori possono essere passati all'elemento del ruolo dello stato in questo modo.
let statusRoleElement = document.querySelector('#applied-filters')
statusRoleElement.style.counterSet = selectData.length
Risultati riflessi nell'elemento role="status"
:checked
fornisce un modo integrato per passare il numero di filtri scelti all'elemento del ruolo dello stato, ma non consente di visualizzare il numero di risultati filtrati.
JavaScript può monitorare le interazioni con le caselle di controllo e, dopo aver filtrato la tabella, aggiungere textContent
come ha fatto l'elemento <select>
.
document
.querySelector('aside form')
.addEventListener('input', e => {
// isotope demo code
let filterResults = IsotopeGrid.getFilteredItemElements().length
document.querySelector('#applied-filters').textContent = `giving ${filterResults} results`
})
Nel complesso, questo lavoro completa l'annuncio "2 filtri che forniscono 25 risultati".
Ora la nostra eccellente esperienza con le tecnologie per la disabilità verrà offerta a tutti gli utenti, indipendentemente dal modo in cui interagiscono con essa.
Conclusione
Ora che sai come ho fatto, come faresti? 🙂
Diversifichiamo i nostri approcci e impariamo tutti i modi per creare sul web. Crea una demo, twittami i link e io la aggiungerò alla sezione dei remix della community di seguito.
Remix della community
Ancora nessun elemento da visualizzare.