Tworzenie komponentu wielokrotnego wyboru

Podstawowy przegląd sposobów tworzenia responsywnego, elastycznego i dostępnego komponentu z wieloznacznikiem do sortowania i filtrowania treści.

W tym poście przedstawię sposoby tworzenia komponentu wielokrotnego wyboru. Wypróbuj wersję demonstracyjną.

Demonstracja

Jeśli wolisz film, oto wersja tego posta w YouTube:

Omówienie

Użytkownicy często widzą wiele produktów, a w takich przypadkach warto udostępnić sposób na skrócenie listy, aby zapobiec nadmiernej ilości opcji. W tym poście na blogu omawiamy interfejs filtrowania jako sposób na ograniczenie liczby opcji. Robi to, prezentując atrybuty produktów, które użytkownicy mogą zaznaczać lub odznaczać, ograniczając liczbę wyników i tym samym ograniczając ilość wyborów.

Interakcje

Celem jest umożliwienie szybkiego przeglądania opcji filtrowania wszystkim użytkownikom i przy użyciu różnych typów danych wejściowych. Będzie to możliwe dzięki parze komponentów, które są elastyczne i dopasowują się do treści. Tradycyjny pasek boczny z polami wyboru dotyczącymi komputerów, klawiatury i czytników ekranu oraz <select multiple> dla użytkowników dotykowych.

Zrzut ekranu porównujący jasny i ciemny motyw na komputerze z paskiem z polami wyboru oraz wersje na iOS i Androida z elementem wielokrotnego wyboru

Decyzja o użyciu wbudowanego elementu wyboru wielokrotnego w przypadku urządzeń dotykowych, a nie komputerów stacjonarnych, pozwala zaoszczędzić czas i wymaga dodatkowych działań, ale zapewnia odpowiednie działanie przy mniejszym zadłużeniu kodu niż tworzenie całego interfejsu responsywnego w jednym komponencie.

Dotyk

Komponent dotykowy oszczędza miejsce i pomaga zwiększyć dokładność interakcji na urządzeniach mobilnych. Oszczędza miejsce, ponieważ pozwala zwijać cały pasek boczny z polem wyboru do wbudowanego <select>przeźroczystego okienka dotykowego. Pomaga to zwiększyć dokładność wprowadzania, wyświetlając dużą nakładkę dotykową.

Podgląd elementu wielokrotnego wyboru w Chrome na Androidzie, iPhonie i iPadzie. Na iPadzie i iPhonie znajduje się przełącznik wielokrotnego wyboru, który jest zoptymalizowany pod kątem rozmiaru ekranu.

Klawiatura i kontroler

Poniżej pokazujemy, jak używać <select multiple> na klawiaturze.

Ta wbudowana opcja wyboru wielokrotnego nie może być stylizowana i jest dostępna tylko w kompaktowym układzie, który nie nadaje się do przedstawiania wielu opcji. Widzisz, że w tym małym polu nie widać wszystkich opcji? Chociaż możesz zmieniać jego rozmiar, w dalszym ciągu nie jest tak przydatna jak pasek boczny z polami wyboru.

Znacznik

Oba komponenty będą się znajdowały w tym samym elemencie <form>. Wyniki tego formularza, niezależnie od tego, czy są to pola wyboru, czy pola wielokrotnego wyboru, będą obserwowane i używane do filtrowania siatki, ale mogą też zostać przesłane na serwer.

<form>

</form>

Komponent Pola wyboru

Grupy pól wyboru powinny być umieszczone w element <fieldset> i przydzielony do <legend>. Gdy kod HTML jest tak sformatowany, czytniki ekranu i FormData automatycznie rozumieją relacje między elementami.

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

Po utworzeniu grupowania dodaj <label><input type="checkbox"> do każdego filtra. Ja użyłem elementu <div>, aby właściwości CSS gap mogły je rozmieścić równomiernie i utrzymać wyrównanie, gdy etykiety będą miały kilka wierszy.

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

Zrzut ekranu z informacjami nakładkowymi dla legendy i elementów pola zawierających informacje o kolorze i nazwie elementu.

Komponent <select multiple>

Rzadko używaną funkcją elementu <select> jest multiple. Jeśli atrybut jest używany z elementem <select>, użytkownik może wybrać wiele elementów z listy. To jak zmiana interakcji z listy rozwijanej na listę z polami wyboru.

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

Aby oznaczyć i utworzyć grupy wewnątrz elementu <select>, użyj elementu <optgroup> i przypisz mu atrybut label oraz wartość. Ten element i wartość atrybutu są podobne do elementów <fieldset><legend>.

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

Teraz dodaj elementy <option> do filtra.

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

Zrzut ekranu przedstawiający renderowanie elementu z możliwością wielokrotnego wyboru na pulpicie.

Śledzenie danych wejściowych za pomocą liczników w celu informowania technologii wspomagających

W tym przypadku do śledzenia i utrzymywania liczby filtrów dla czytników ekranu oraz innych technologii wspomagających stosuje się technikę statusu roli. Film w YouTube demonstruje tę funkcję. Integracja zaczyna się od kodu HTML i atrybutu role="status".

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

Ten element odczyta na głos zmiany wprowadzone w treści. Możemy aktualizować zawartość za pomocą liczników CSS, gdy użytkownicy będą korzystać z pól wyboru. W tym celu trzeba najpierw utworzyć licznik z nazwą w elemencie nadrzędnym danych wejściowych i elementów stanu.

aside {
  counter-reset: filters;
}

Domyślnie liczba wynosi 0, co jest bardzo dobre. Domyślnie w tym projekcie nic nie jest określone jako :checked.

Następnie, aby zwiększyć wartość nowo utworzonego licznika, będziemy kierować reklamy na elementy podrzędne elementu <aside>, które są :checked. Gdy użytkownik zmienia stan danych wejściowych, licznik filters będzie się zwiększał.

aside :checked {
  counter-increment: filters;
}

CSS wie teraz, że ogólna liczba pól wyboru w interfejsie jest równa 0, a element stanu role jest pusty i oczekuje na wartości. Ponieważ CSS przechowuje wynik w pamięci, funkcja counter() umożliwia dostęp do wartości z zawartości pseudoelementu:

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

Kod HTML elementu roli stanu będzie teraz ogłaszać w narzędziach do odczytu ekranu „2 filtry”. To dobry początek, ale możemy zrobić więcej, np. udostępnić podsumowanie wyników zaktualizowanych przez filtry. Wykorzystamy do tego JavaScript, ponieważ nie można tego zrobić za pomocą liczników.

Zrzut ekranu z czytnikiem ekranu MacOS, który informuje o liczbie aktywnych filtrów.

Tworzenie emocji

Algorytm liczników świetnie współpracował z CSS nesting-1, ponieważ udało mi się umieścić całą logikę w jednym bloku. Przenośna i scentralizowana funkcja odczytu i aktualizowania.

aside {
  counter-reset: filters;

  & :checked {
    counter-increment: filters;
  }

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

Układy

W tej sekcji opisano układy między tymi 2 komponentami. Większość stylów układu dotyczy komponentu pola wyboru na komputerze.

Formularz

Aby zoptymalizować czytelność i czytelność dla użytkowników, formularz ma maksymalną szerokość 30 znaków, co zasadniczo ustawia szerokość linii optycznych dla każdej etykiety filtra. Formularz używa układu siatki i właściwości gap, aby rozmieszczać pola danych w odstępach.

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

Element <select>

Lista etykiet i pola wyboru zajmują zbyt dużo miejsca na urządzeniach mobilnych. Dlatego układ sprawdza, jakie urządzenie wskazujące jest używane przez użytkownika, aby zmienić sposób obsługi dotykowej.

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

Wartość coarse oznacza, że użytkownik nie będzie mógł sterować ekranem z dużą precyzją za pomocą głównego urządzenia wejściowego. Na urządzeniu mobilnym wartość wskaźnika często wynosi coarse, ponieważ główną metodą interakcji jest dotyk. Na komputerze wartość wskaźnika często wynosi fine, ponieważ często jest podłączona mysz lub inne urządzenie wejściowe o wysokiej precyzji.

Pola zbiorcze

Domyślny styl i układ elementu <fieldset> z atrybutem <legend> jest unikalny:

Zrzut ekranu pokazujący domyślne style pola formularza i legendy.

Normalnie, aby rozmieścić elementy podrzędne, użyjesz właściwości gap, ale unikalne położenie elementu <legend> utrudnia utworzenie równomiernie rozmieszczonych elementów podrzędnych. Zamiast gap używane są selektor sąsiedniego elementu bratamargin-block-start.

fieldset {
  padding: 2ch;

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

W ten sposób nie trzeba dostosowywać miejsca dla <legend>, kierując go tylko do dzieci z grupy <div>.

Zrzut ekranu pokazujący odstęp między polami danych, ale nie legendy.

Etykieta i pole wyboru filtra

Jako element podrzędny elementu <fieldset> i mieszczący się w maksymalnej szerokości elementu 30ch, tekst etykiety może się przewijać, jeśli jest zbyt długi. Zawijanie tekstu jest świetne, ale niedopasowanie między tekstem a polem wyboru nie. Do tego celu idealnie nadaje się flexbox.

fieldset > div {
  display: flex;
  gap: 2ch;
  align-items: baseline;
}
Zrzut ekranu pokazujący wyrównanie znacznika do pierwszego wiersza tekstu w przypadku zawijania wielu wierszy.
Zagraj więcej w tym Codepen

Animowana siatka

Animację układu wykonuje Isotope. Wydajny i zaawansowany wtyczka do interaktywnego sortowania i filtrowania.

JavaScript

JavaScript pomaga nie tylko w zarządzaniu schludną animowaną, interaktywną siatką, ale także w dopracowaniu drobnych elementów.

Normalizacja danych wejściowych użytkownika

Ten projekt ma 1 formularz z 2 różnymi sposobami wprowadzania danych, które nie są serializowane. Używając kodu JavaScript, możemy normalizować dane.

Zrzut ekranu konsoli JavaScript DevTools, na którym widać wyniki celów i znormalizowane dane.

Zdecydowaliśmy się dopasować strukturę danych elementu <select> do struktury pól wyboru w grupach. W tym celu do elementu <select> dodawany jest detektor zdarzenia input, który jest następnie mapowany na element 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
  }, [])
})

Teraz możesz bezpiecznie przesłać formularz. W przypadku tej wersji demonstracyjnej możesz zapytać Isotope, jak ma filtrować dane.

Kończenie elementu roli stanu

Element zlicza i podaje liczbę filtrów tylko na podstawie interakcji z polem wyboru, ale uznaliśmy, że warto dodatkowo udostępnić liczbę wyników i upewnić się, że uwzględnione są też elementy <select>.

Wybór elementu <select> widoczny w tabeli counter()

W sekcji normalizacji danych na podstawie danych wejściowych został już utworzony odbiorca. Na końcu tej funkcji znana jest liczba wybranych filtrów i liczba wyników dla tych filtrów. W ten sposób wartości można przekazywać do elementu „stan”.

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

Wyniki odzwierciedlone w elemencie role="status"

:checked zapewnia wbudowany sposób przekazywania liczby wybranych filtrów do elementu roli stanu, ale nie zapewnia widoczności liczby wyników po odfiltrowaniu. Kod JavaScript może sprawdzać interakcje z polami wyboru i po odfiltrowaniu siatki dodawać textContent, tak jak to robi element <select>.

document
  .querySelector('aside form')
  .addEventListener('input', e => {
    // isotope demo code
    let filterResults = IsotopeGrid.getFilteredItemElements().length
    document.querySelector('#applied-filters').textContent = `giving ${filterResults} results`
})

W ten sposób zakończymy proces wdrażania ogłoszenia „2 filtry, które dają 25 wyników”.

Zrzut ekranu z czytnikiem ekranu na macOS ogłaszającym wyniki.

Teraz wszyscy użytkownicy będą mogli korzystać z naszej doskonałej technologii wspomagającej.

Podsumowanie

Wiesz już, jak to zrobiłem. Jak Ty? 🙂

Zróżnicujemy nasze podejścia i poznamy wszystkie sposoby tworzenia stron internetowych. Utwórz wersję demonstracyjną, wyślij mi linki, a ja dodam je do sekcji z remiksami społeczności.

Remiksy społeczności

Na razie jest tu pusto