Compila un componente de selección múltiple

Una descripción general fundamental de cómo compilar un componente de selección múltiple responsivo, adaptable y accesible para ordenar y filtrar las experiencias del usuario.

En esta publicación, quiero compartir las ideas sobre una forma de crear un componente de selección múltiple. Prueba la demostración.

Demostración

Si prefieres ver un video, aquí tienes una versión de YouTube de esta publicación:

Descripción general

A menudo, a los usuarios se les presentan elementos, que a veces son muchos elementos, por lo que puede ser una buena idea proporcionar una forma de reducir la lista para evitar la sobrecarga de opciones. En esta entrada de blog, se explora la IU de filtrado como una forma de reducir las opciones. Para ello, presenta los atributos del elemento que los usuarios pueden seleccionar o anular la selección, lo que reduce los resultados y, por lo tanto, la sobrecarga de opciones.

Interacciones

El objetivo es habilitar el cruce rápido de opciones de filtro para todos los usuarios y sus diferentes tipos de entrada. Esto se entregará con un par de componentes adaptables y responsivos. Una barra lateral tradicional de casillas de verificación para computadoras, teclados y lectores de pantalla, y un <select multiple> para usuarios táctiles.

Captura de pantalla comparativa que muestra computadoras de escritorio claros y oscuros con una barra lateral de casillas de verificación en comparación con iOS y Android para dispositivos móviles con un elemento de selección múltiple.

Esta decisión de usar la selección múltiple integrada para el modo táctil y no para las computadoras de escritorio ahorra trabajo y crea trabajo, pero creo que ofrece experiencias adecuadas con menos deuda de código que compilar la experiencia responsiva completa en un solo componente.

Tacto

El componente táctil ahorra espacio y ayuda a mejorar la precisión de la interacción del usuario en dispositivos móviles. Para ahorrar espacio, contrae una barra lateral completa de casillas de verificación en una experiencia táctil de superposición integrada de <select>. Ayuda a la exactitud de la entrada, ya que muestra una gran experiencia de superposición táctil que proporciona el sistema.

Vista previa de una captura de pantalla del elemento de selección múltiple en Chrome en Android, iPhone y iPad. El iPad y el iPhone tienen activada la selección múltiple y cada uno obtiene una experiencia única optimizada para el tamaño de la pantalla.

Teclado y control de juegos

A continuación, se muestra una demostración de cómo usar un <select multiple> desde el teclado.

Esta selección múltiple integrada no se puede diseñar y solo se ofrece en un diseño compacto que no es adecuado para presentar muchas opciones. ¿Ves que no puedes ver la amplitud de opciones en esa pequeña caja? Si bien puedes cambiar su tamaño, aún no se puede usar tanto como una barra lateral de casillas de verificación.

Marca

Ambos componentes estarán en el mismo elemento <form>. Los resultados de este formulario, ya sean casillas de verificación o una selección múltiple, se observarán y se usarán para filtrar la cuadrícula, pero también se podrían enviar a un servidor.

<form>

</form>

Componente de casillas de verificación

Los grupos de casillas de verificación deben unirse en un elemento <fieldset> y recibir una <legend>. Cuando HTML se estructura de esta manera, los lectores de pantalla y FormData comprenderán automáticamente la relación entre los elementos.

<form>
  <fieldset>
    <legend>New</legend>
    … checkboxes …
  </fieldset>
</form>

Una vez que esté la agrupación, agrega <label> y <input type="checkbox"> para cada uno de los filtros. Elegí unir el mío en un <div> para que la propiedad gap de CSS pueda espaciarlos de manera uniforme y mantener la alineación cuando las etiquetas tengan varias líneas.

<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>

Una captura de pantalla con una superposición informativa de los elementos de la leyenda y el conjunto de campos, que muestra el color y el nombre del elemento.

Componente <select multiple>

Un atributo del elemento <select> que se usa muy poco es multiple. Cuando se usa el atributo con un elemento <select>, el usuario puede elegir muchos de los elementos de la lista. Es como cambiar la interacción de una lista de radio a una lista de casillas de verificación.

<form>
  <select multiple="true" title="Filter results by category">
    …
  </select>
</form>

Para etiquetar y crear grupos dentro de una <select>, usa el elemento <optgroup> y asígnale un atributo y un valor label. Este valor del elemento y el atributo son similares a los elementos <fieldset> y <legend>.

<form>
  <select multiple="true" title="Filter results by category">
    <optgroup label="New">
      …
    </optgroup>
  </select>
</form>

Ahora, agrega los elementos <option> para el 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>

Captura de pantalla de la renderización en computadoras de escritorio de un elemento de selección múltiple.

Seguimiento de las entradas con contadores para informar a la tecnología de asistencia

La técnica de función de estado se usa en esta experiencia del usuario para realizar un seguimiento y mantener el recuento de filtros para lectores de pantalla y otras tecnologías de accesibilidad. El video de YouTube demuestra la función. La integración comienza con HTML y el atributo role="status".

<div role="status" class="sr-only" id="applied-filters"></div>

Este elemento leerá en voz alta los cambios realizados en el contenido. Podemos actualizar el contenido con contadores de CSS a medida que los usuarios interactúan con las casillas de verificación. Para ello, primero debemos crear un contador con un nombre en un elemento superior de las entradas y el elemento de estado.

aside {
  counter-reset: filters;
}

De forma predeterminada, el recuento será 0, lo cual es genial, nada es :checked de forma predeterminada en este diseño.

A continuación, para aumentar el contador recién creado, nos enfocaremos en elementos secundarios del elemento <aside> que son :checked. A medida que el usuario cambie el estado de las entradas, el contador filters contará.

aside :checked {
  counter-increment: filters;
}

CSS ahora reconoce el recuento general de la IU de la casilla de verificación y el elemento de la función de estado está vacío y a la espera de valores. Dado que CSS mantiene el recuento en la memoria, la función counter() permite acceder al valor desde el contenido de los pseudoelementos:

aside #applied-filters::before {
  content: counter(filters) " filters ";
}

El código HTML del elemento de función de estado ahora anunciará "2 filtros " a un lector de pantalla. Este es un buen comienzo, pero podemos hacerlo mejor, como compartir el recuento de los resultados que se actualizaron los filtros. Lo haremos desde JavaScript, ya que está fuera de lo que pueden hacer los contadores.

Captura de pantalla del lector de pantalla MacOS donde se anuncia la cantidad de filtros activos.

Anidación de la actividad

El algoritmo de los contadores resultaba muy bueno con CSSnesting-1, ya que pude poner toda la lógica en un bloque. Parece portátil y centralizada para leer y actualizar.

aside {
  counter-reset: filters;

  & :checked {
    counter-increment: filters;
  }

  & #applied-filters::before {
    content: counter(filters) " filters ";
  }
}

Diseños

En esta sección, se describen los diseños entre los dos componentes. La mayoría de los diseños de diseño son para el componente de la casilla de verificación de escritorio.

El formulario

Con el objetivo de optimizar la legibilidad y la capacidad de lectura para los usuarios, se le otorga al formulario un ancho máximo de 30 caracteres, lo que básicamente establece un ancho de línea óptica para cada etiqueta de filtro. El formulario usa un diseño de cuadrícula y la propiedad gap para espaciar los conjuntos de campos.

form {
  display: grid;
  gap: 2ch;
  max-inline-size: 30ch;
}

El elemento <select>

La lista de etiquetas y las casillas de verificación ocupan demasiado espacio en los dispositivos móviles. Por lo tanto, el diseño verifica el dispositivo apuntador principal del usuario para cambiar la experiencia táctil.

@media (pointer: coarse) {
  select[multiple] {
    display: block;
  }
}

Un valor de coarse indica que el usuario no podrá interactuar con la pantalla con grandes cantidades de precisión con su dispositivo de entrada principal. En un dispositivo móvil, el valor del puntero suele ser coarse, ya que la interacción principal es táctil. En un dispositivo de escritorio, el valor del puntero suele ser fine, ya que es común tener conectado un mouse o algún otro dispositivo de entrada de alta precisión.

Los conjuntos de campos

El estilo y el diseño predeterminados de una <fieldset> con un <legend> son únicos:

Captura de pantalla de los estilos predeterminados para un conjunto de campos y una leyenda.

Por lo general, para espaciar mis elementos secundarios, usaría la propiedad gap, pero el posicionamiento único de <legend> dificulta la creación de un conjunto de elementos secundarios espaciado uniformemente. En lugar de gap, se usan el selector del mismo nivel adyacente y margin-block-start.

fieldset {
  padding: 2ch;

  & > div + div {
    margin-block-start: 2ch;
  }
}

De esta manera, se evita que <legend> se ajuste su espacio mediante la orientación solo a los elementos secundarios <div>.

Captura de pantalla en la que se muestra el espaciado de los márgenes entre las entradas, pero no la leyenda.

La etiqueta del filtro y la casilla de verificación

Como elemento secundario directo de un <fieldset> y dentro del ancho máximo del 30ch del formulario, el texto de la etiqueta puede ajustarse si es demasiado largo. Unir texto es genial, pero la desalineación entre el texto y la casilla de verificación no lo es. Flexbox es ideal para esto.

fieldset > div {
  display: flex;
  gap: 2ch;
  align-items: baseline;
}
Captura de pantalla en la que se muestra cómo la marca de verificación se alinea con la primera línea de texto en una situación de ajuste de varias líneas.
Juega más en este CodePen

La cuadrícula animada

Isotope realiza la animación de diseño. Un complemento potente y de alto rendimiento para ordenar y filtrar de manera interactiva.

JavaScript

Además de ayudar a organizar una buena cuadrícula interactiva y animada, JavaScript se usa para pulir un par de asperezas.

Normalización de las entradas del usuario

Este diseño tiene un formulario con dos formas diferentes de proporcionar entrada y no serializan la misma información. Sin embargo, con JavaScript, podemos normalizar los datos.

Captura de pantalla de la consola de JavaScript de Herramientas para desarrolladores que muestra el objetivo, resultados de datos normalizados.

Elegí alinear la estructura de datos del elemento <select> con la estructura de las casillas de verificación agrupadas. Para ello, se agrega un objeto de escucha de eventos input al elemento <select>, en cuyo punto se asigna 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
  }, [])
})

Ahora, es seguro enviar el formulario o, en el caso de esta demostración, indicarle a Isotope qué filtrar.

Finalización del elemento de rol de estado

El elemento solo cuenta y anuncia el recuento de filtros en función de la interacción de las casillas de verificación, pero me parece que era una buena idea compartir además la cantidad de resultados y asegurarse de que también se contaran las opciones del elemento <select>.

La elección del elemento <select> se refleja en el counter()

En la sección de normalización de datos, ya se creó un objeto de escucha en la entrada. Al final de esta función, se conoce el número de filtros elegidos y la cantidad de resultados de esos filtros. Los valores se pueden pasar al elemento de función de estado de esta manera.

let statusRoleElement = document.querySelector('#applied-filters')
statusRoleElement.style.counterSet = selectData.length

Resultados reflejados en el elemento role="status"

:checked proporciona una forma integrada de pasar la cantidad de filtros elegidos al elemento de la función de estado, pero carece de visibilidad para la cantidad de resultados filtrados. JavaScript puede buscar la interacción con las casillas de verificación y, después de filtrar la cuadrícula, agregar textContent como lo hizo el 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`
})

En conjunto, este trabajo completa el anuncio "2 filtros que dan 25 resultados".

Captura de pantalla del lector de pantalla MacOS que anuncia los resultados.

Ahora, nuestra excelente experiencia de tecnología de accesibilidad se ofrecerá a todos los usuarios, sin importar cómo interactúen con ella.

Conclusión

Ahora que sabes cómo lo hice, ¿cómo lo harías 🙂

Diversifiquemos nuestros enfoques y aprendamos todas las formas de desarrollar en la Web. Crea una demostración, twittea vínculos y la agregaré a la sección de remixes de la comunidad a continuación.

Remixes de la comunidad

Aún no hay nada que ver aquí.