Background image
11.11.2024, Kai Gertz

select-improved: eine verbesserte select-Komponente

Aus der Arbeit bei Tojio ist eine verbessere select-Komponente entstanden, die wir als Open Source Widget veröffentlichen.

Wir haben bei Tojio ein angepasstes select-Widget speziell für Mehrfachauswahlen erstellt. Das Problem mit dem herkömmlichen select-Element: 

  • es ist nur sehr schwer visuell anpassbar
  • es lässt sich nicht filtern (v.a. bei langen Listen sehr hinderlich)
  • wenn eine Mehrfachauswahl möglich ist, sieht man die gewählten Optionen nach dem Zuklappen der List nicht mehr

Für den Veranstaltungskalender für die Woche der Klimaanpassung  (ausgerichtet vom ZentrumKlimaAnpassung) haben wir verschiedene Filtermöglichkeiten eingebaut: prominent darüber lässt sich nach den einzelnen Wochentagen filtern. Darunter verbirgt sich hinter einem Button "Filter einstellen" ein zunächst zugeklappter Bereich mit weiteren Möglichkeiten, Veranstaltungen nach bestimmten Kriterien (z.B. Bundesland) zu filtern.

Für Filter dieser Art, die lange Select-Listen verwenden, haben wir eine stark verbesserte Web Component erstellt, die das native select-Element ersetzt. 

Screenshot einer verbesserten Select Komponente mit Filter und Ablagebereich für die ausgewählten Optionen

Unser verbessertes select widget: select-improved

Erweitern des nativen Select-Elements

Unser select-improved ist eine Web Component, die das native select-Element erweitert.

class SelectImproved extends HTMLSelectElement {

[...]

}
customElements.define('select-improved', SelectImproved, { extends: 'select' });

Funktionsweise der Komponente

Eine Web Component, die bestehende Elemente erweitert, wird aber nicht wie gewohnt mit ihrem eigenen Tag-Namen eingebettet, sondern das ursprüngliche Element wird um das Attribut is erweitert, in dem die Erweiterungsklasse angegeben wird: <select is="select-improved">

Das bedeutet, dass wir an den Stellen, die das neue Element verwenden sollen, das Twig Template abändern und das Attribut dort einfügen müssen (s.u.).

{% set replacement_info =  'is=select-improved' %}
{{ attach_library('zka/custom-elements-polyfill') }}
{{ attach_library('zka/select-improved') }}

{% apply spaceless %}
  <select{{ attributes }} {{ replacement_info }}>

    {# [...] options as usual  #}
 
  </select>
{% endapply %}

Die erweiterte Komponente übernimmt die Optionen, wie sie dem Select-Element übergeben werden und baut eine neue, visuelle Repräsentation mit Filter, Liste und einem Bereich zur Anzeige ausgewählter Optionen (bei Mehrfachauswahl) innerhalb der shadow root der Komponente auf. 

Polyfill für Browser, die keine Erweiterungen von nativen Elementen unterstützen

Leider unterstützen nicht alle Browser (insbesondere nicht Safari auf MacOS / iOS, ebensowenig wie die WebView auf iOS) das Erweitern bestehender HTML-Elemente (Schade Apple, Safari war einmal ein wirklich schönes, progressives Stück Software). Ein längerer Issue im WebKit-Projekt stellt die Diskussion dazu dar. 

Damit unser custom select Element also auch in Safari funktioniert, muss ein Polyfill eingesetzt werden, der diese Funktionalität auch in Safari ermöglicht. Der Polyfill stammt aus dem Ungap-Projekt und wir definieren dafür eine eigene Library, die zuerst geladen wird. Damit diese Library verfügbar ist, bevor überhaupt ein custom element instanziiert werden kann, forcieren wir, dass sie im Kopf des Dokuments schon geladen wird:

custom-elements-polyfill:
  version: 1.0
  # it is crucial that this available before any other custom element
  # stuff so move it into the header 
  header: true
  js:
    # polyfill needed for safari
    components/select-improved/custom-elements.1.3.0.min.js: { minified: true }

select-improved:
  version: 1.0
  js:
    # definition of the web component
    components/select-improved/select-improved.js: {}
  css:
    theme:
      # this css holds the custom properties to adjust styling
      components/select-improved/select-improved.css: {}

Unsere verbesserte select-Komponente kann im Gitlab Repo zum Gebrauch heruntergeladen werden.