Una panoramica di base su come creare un componente a selezione multipla, adattabile, adattivo e accessibile per ordinare e filtrare le esperienze utente.
In questo post vorrei condividere con voi la possibilità di creare un componente a selezione multipla. Prova la demo.
Se preferisci i video, ecco una versione di YouTube di questo post:
Panoramica
Agli utenti vengono spesso presentati degli elementi, a volte molti elementi, e in questi casi può essere una buona idea fornire un modo per ridurre l'elenco ed evitare il sovraccarico di scelte. Questo post del blog esplora l'interfaccia utente di filtro come un modo per ridurre le scelte. Per farlo, presenta gli attributi degli elementi che gli utenti possono selezionare o deselezionare, riducendo i risultati e di conseguenza il sovraccarico di scelte.
Interazioni
L'obiettivo è consentire un attraversamento rapido delle opzioni di filtro per tutti gli utenti e i vari tipi di input. Verrà fornita con una coppia di componenti
adattabili e reattivi. Una barra laterale tradizionale di caselle di controllo per computer, tastiere e screen reader, e un elemento <select
multiple>
per gli utenti touch.
Questa decisione di usare la selezione multipla integrata per il tocco, e non per il desktop, consente di risparmiare lavoro e lavoro, ma credo che offra esperienze appropriate con meno debito di codice rispetto alla creazione dell'intera esperienza reattiva in un solo componente.
Tocco
Il componente tocco consente di risparmiare spazio e contribuisce alla precisione dell'interazione dell'utente su
dispositivi mobili. Consente di risparmiare spazio comprimendo un'intera barra laterale di caselle di controllo in un'esperienza touch in overlay integrata di <select>
. L'input è più preciso, grazie all'ampio overlay
tattile fornito dal sistema.
Tastiera e gamepad
Di seguito è riportata una dimostrazione di come utilizzare <select multiple>
dalla tastiera.
Questa selezione multipla integrata non può avere uno stile ed è offerta solo in un layout compatto non adatto a presentare molte opzioni. Hai visto come non riesci a vedere la varietà di opzioni in quella piccola scatola? Pur essendo possibile modificare le dimensioni, non è utilizzabile come barra laterale delle caselle di controllo.
Markup
Entrambi i componenti saranno contenuti nello stesso elemento <form>
. I risultati di questo modulo, che siano caselle di controllo o una 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 aggregati in un elemento <fieldset>
e devono essere assegnati un <legend>
.
Se l'HTML è strutturato in questo modo, gli screen reader e FormData comprenderanno automaticamente la relazione degli elementi.
<form>
<fieldset>
<legend>New</legend>
… checkboxes …
</fieldset>
</form>
Una volta attivato il raggruppamento, aggiungi <label>
e <input type="checkbox">
per
ciascuno dei filtri. Ho scelto di aggregare i miei dati in un elemento <div>
in modo che la proprietà CSS gap
possa distribuirli in modo uniforme e mantenere l'allineamento quando le etichette sono 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à dell'elemento <select>
utilizzata raramente è multiple
.
Quando l'attributo viene utilizzato in combinazione con un elemento <select>
, l'utente può sceglierne
molti dall'elenco. È come cambiare l'interazione da un elenco
di pulsanti 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 elemento <select>
, utilizza l'elemento
<optgroup>
e assegnagli un attributo e un valore label
. Questo elemento e 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 informare le tecnologie per la disabilità
La tecnica del ruolo di stato viene utilizzata in questa esperienza utente per monitorare e mantenere il conteggio dei filtri per screen reader e altre tecnologie per la disabilità. Il video di YouTube mostra 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 i contatori CSS man mano che gli utenti interagiscono con le caselle di controllo. Per farlo, dobbiamo prima creare un contatore con un nome su un elemento principale degli input e dell'elemento di stato.
aside {
counter-reset: filters;
}
Per impostazione predefinita, il conteggio sarà 0
, il che è ottimo. Per impostazione predefinita, in questo design non è
nullo nulla è :checked
.
Successivamente, per incrementare il contatore appena creato, sceglieremo come target gli elementi secondari dell'elemento
<aside>
che sono :checked
. Quando l'utente cambia lo stato degli input,
viene conteggiato il contatore filters
.
aside :checked {
counter-increment: filters;
}
CSS ora è a conoscenza del conteggio generale dell'interfaccia utente delle caselle di controllo e l'elemento del ruolo di stato è vuoto e in attesa di valori. Poiché CSS mantiene il conteggio in memoria, la funzione counter()
consente di accedere al valore dai contenuti di pseudo elementi:
aside #applied-filters::before {
content: counter(filters) " filters ";
}
Nel codice HTML dell'elemento del ruolo di stato ora verrà annunciato "2 filtri" a uno screen reader. Questo è un buon inizio, ma possiamo fare di meglio, ad esempio condividere il conteggio dei risultati aggiornati dai filtri. Questa operazione verrà eseguita da JavaScript, perché non è applicabile ai contatori.
Emozione nidificata
L'algoritmo dei contatori sembrava ottimo con CSS nesting-1, dato che ho potuto inserire tutta la logica in un unico blocco. È portatile e centralizzato per la lettura e l'aggiornamento.
aside {
counter-reset: filters;
& :checked {
counter-increment: filters;
}
& #applied-filters::before {
content: counter(filters) " filters ";
}
}
Layout
In questa sezione vengono descritti i layout tra i due componenti. La maggior parte degli stili di layout è per il componente desktop con caselle di controllo.
Il modulo
Per ottimizzare leggibilità e leggibilità per gli utenti, il modulo ha una larghezza massima di 30 caratteri, in sostanza impostando una larghezza ottica della linea per ogni etichetta del filtro. Il modulo utilizza il layout a griglia e la proprietà gap
per distribuire i set di campi.
form {
display: grid;
gap: 2ch;
max-inline-size: 30ch;
}
Elemento <select>
Sia l'elenco di etichette sia le caselle di controllo occupano troppo spazio sui dispositivi mobili. Pertanto, il layout controlla per vedere il dispositivo di puntamento principale dell'utente per modificare l'esperienza del tocco.
@media (pointer: coarse) {
select[multiple] {
display: block;
}
}
Un valore coarse
indica che l'utente non sarà in grado di interagire con lo schermo in modo estremamente preciso con il dispositivo di input principale. Su un
dispositivo mobile, il valore del puntatore è spesso coarse
, in quanto l'interazione principale
è il tocco. Su un dispositivo desktop, il valore del puntatore è spesso fine
, perché di solito è collegato un mouse o un altro dispositivo di input ad alta precisione.
I set di campi
Lo stile e il layout predefiniti di <fieldset>
con <legend>
sono univoci:
Normalmente, per distribuire gli elementi secondari, utilizzerei la proprietà gap
, ma il posizionamento univoco
di <legend>
rende difficile creare un insieme di elementi secondari equidistanti. Al posto di gap
, vengono utilizzati il selettore di pari livello e margin-block-start
.
fieldset {
padding: 2ch;
& > div + div {
margin-block-start: 2ch;
}
}
In questo modo, non è possibile modificare lo spazio di <legend>
scegliendo come target solo
<div>
elementi secondari.
L'etichetta del filtro e la casella di controllo
Come elemento secondario diretto di un elemento <fieldset>
e all'interno della larghezza massima del 30ch
del modulo, il testo dell'etichetta potrebbe essere a capo se troppo lungo. Il testo a capo è ottimo, ma non ci sono disallineamenti tra testo e casella di controllo. Flexbox è l'ideale.
fieldset > div {
display: flex;
gap: 2ch;
align-items: baseline;
}
La griglia animata
L'animazione del layout è eseguita da Isotope. Plug-in potente e potente per l'ordinamento e i filtri interattivi.
JavaScript
Oltre ad aiutarti a orchestrare una griglia animata e interattiva, JavaScript viene utilizzato per perfezionare qualche dettaglio.
Normalizzazione dell'input utente
Questo design ha un modulo con due diversi modi per fornire input che non serializzano lo stesso. Tuttavia, con JavaScript possiamo normalizzare i dati.
Ho scelto di allineare la struttura dei dati dell'elemento <select>
alla struttura delle caselle di controllo raggruppate. A questo scopo, all'elemento <select>
viene aggiunto un listener di eventi input
, a quel punto viene mappato selectedOptions
.
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 oppure, nel caso di questa demo, istruisci Isotope su cosa filtrare in base a cosa.
Completare l'elemento del ruolo di stato
L'elemento conta solo e annuncia il conteggio dei 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 assicurarsi 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 listener per l'input. Al termine di questa funzione, sono noti il numero di filtri scelti e il numero di risultati per tali filtri. I valori possono essere passati all'elemento ruolo stato come questo.
let statusRoleElement = document.querySelector('#applied-filters')
statusRoleElement.style.counterSet = selectData.length
Risultati riportati nell'elemento role="status"
:checked
fornisce un modo integrato per passare il numero di filtri scelti all'elemento di ruolo dello stato, ma non ha visibilità rispetto al numero di risultati filtrati.
JavaScript può controllare l'interazione con le caselle di controllo e, dopo aver filtrato la griglia, 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`
})
Complessivamente, questo lavoro completa l'annuncio "2 filtri per 25 risultati".
Ora la nostra eccellente esperienza relativa alle tecnologie per la disabilità verrà offerta a tutti gli utenti, indipendentemente dal modo in cui vi interagiscono.
Conclusione
Ora che sai come ci sono riuscito, come faresti? 🙂
Diversifica i nostri approcci e scopriamo tutti i modi per creare sul web. Crea una demo, inviami un tweet con i link e lo aggiungerò alla sezione Remix della community di seguito.
Remix della community
Ancora niente da visualizzare