Anpassbare `select`-Elemente
Dieser Artikel erklärt, wie Sie dedizierte, moderne HTML- und CSS-Features nutzen können, um vollständig anpassbare <select>
-Elemente zu erstellen. Dies beinhaltet die vollständige Kontrolle über die Gestaltung des Auswahl-Buttons, des Dropdown-Pickers, des Pfeilsymbols, des Häkchens für die aktuelle Auswahl und jedes einzelne <option>
-Element.
Hintergrund
Traditionell war es schwierig, das Aussehen von <select>
-Elementen anzupassen, da sie interne Bestandteile enthalten, die auf Betriebssystemebene gestaltet sind und die nicht mit CSS gezielt angesprochen werden können. Dazu gehören der Dropdown-Picker, das Pfeilsymbol usw.
Zuvor war die beste verfügbare Option – abgesehen von der Verwendung einer benutzerdefinierten JavaScript-Bibliothek – der Einsatz des appearance
-Werts none
auf dem <select>
-Element, um einige Betriebssystem-spezifische Gestaltungen zu entfernen und dann CSS zu verwenden, um die gestaltbaren Teile anzupassen. Diese Technik wird im Erweitertes Formularstyling erklärt.
Anpassbare <select>
-Elemente bieten eine Lösung für diese Probleme. Sie ermöglichen es Ihnen, Beispiele wie das folgende nur mit HTML und CSS zu erstellen, die in unterstützenden Browsern vollständig angepasst sind. Dies umfasst Layout von <select>
und Dropdown-Picker, Farbschema, Symbole, Schriftarten, Übergänge, Positionierung und Markierungen zur Kennzeichnung des ausgewählten Symbols und mehr.
Darüber hinaus bieten sie eine progressive Verbesserung der vorhandenen Funktionalität und fallen in nicht unterstützenden Browsern auf "klassische" select
-Elemente zurück.
Sie erfahren, wie Sie dieses Beispiel in den nachfolgenden Abschnitten erstellen können.
Welche Features umfasst ein anpassbares select
?
Sie können anpassbare <select>
-Elemente mit folgenden HTML- und CSS-Features erstellen:
- Gewöhnliche
<select>
,<option>
und<optgroup>
-Elemente. Diese funktionieren genauso wie in "klassischen"select
, außer dass sie zusätzliche zulässige Inhaltstypen haben. - Ein
<button>
-Element, das als erstes Kind innerhalb des<select>
-Elements enthalten ist, was in "klassischen"select
-Elementen nicht vorher erlaubt war. Wenn dies eingefügt wird, ersetzt es die Standard-Button-Darstellung des geschlossenen<select>
-Elements. Dies wird allgemein als select button bezeichnet (da es der Button ist, den Sie drücken müssen, um den Dropdown-Picker zu öffnen).Hinweis: Der Select-Button ist standardmäßig inert, so dass, wenn interaktive Kinder (zum Beispiel Links oder Buttons) in ihm enthalten sind, er weiterhin wie ein einzelner Button für Interaktionszwecke behandelt wird – zum Beispiel werden die untergeordneten Elemente nicht fokussierbar oder anklickbar sein.
- Das
<selectedcontent>
-Element kann optional innerhalb des ersten Kind-<button>
-Elements des<select>
-Elements eingefügt werden, um den aktuell ausgewählten Wert innerhalb des geschlossenen<select>
-Elements anzuzeigen. Dieses enthält einen Klon des aktuell ausgewählten<option>
-Elementinhalts (erstellt unter der Haube mitcloneNode()
). - Das
::picker(select)
-Pseudo-Element, das den gesamten Inhalt des Pickers anvisiert. Dies schließt alle Elemente innerhalb des<select>
-Elements ein, außer das erste Kind-<button>
. - Der
appearance
-Eigenschaftswertbase-select
, der das<select>
-Element und das::picker(select)
-Pseudo-Element in die vom Browser definierten Standardstile und -verhalten für anpassbareselect
-Elemente einbindet. - Die
:open
-Pseudo-Klasse, die den Select-Button anvisiert, wenn der Picker (::picker(select)
) geöffnet ist. - Das
::picker-icon
-Pseudo-Element, das das Symbol im Select-Button anvisiert – den Pfeil, der nach unten zeigt, wenn das Select geschlossen ist. - Die
:checked
-Pseudo-Klasse, die das aktuell ausgewählte<option>
-Element anvisiert. - Das
::checkmark
-Pseudo-Element, das das Häkchen im aktuell ausgewählten<option>
-Element anvisiert, um eine visuelle Anzeige des aktuell ausgewählten Elements zu bieten.
Zusätzlich haben das <select>
-Element und sein Dropdown-Picker das folgende Verhalten, das ihnen automatisch zugewiesen wird:
- Sie haben eine Aufrufer-/Pop-over-Beziehung, wie sie von der Popover API spezifiziert wird, die die Möglichkeit bietet, den Picker beim Öffnen über die
:popover-open
-Pseudo-Klasse auszuwählen. Weitere Details zum Popover-Verhalten finden Sie unter Verwendung der Popover API. - Sie haben einen impliziten Ankerbezug, was bedeutet, dass der Picker automatisch mit dem
<select>
-Element über die CSS-Ankerpositionierung verbunden ist. Die Standardstile des Browsers positionieren den Picker relativ zum Button (dem Anker) und Sie können diese Position anpassen, wie im Abschnitt Positionierung von Elementen relativ zu ihrem Anker erklärt. Die Standardstile des Browsers definieren auch einige Fallbacks für die Positionierung, um den Picker neu zu positionieren, wenn er Gefahr läuft, den Viewport zu überlaufen. Fallbacks für die Positionssuche werden im Abschnitt Überlauf behandeln: Fallbacks und bedingtes Verstecken erklärt.
Hinweis:
Sie können die Browser-Unterstützung für anpassbare <select>
-Elemente überprüfen, indem Sie die Browser-Kompatibilitätstabellen auf den Referenzseiten für verwandte Features wie <selectedcontent>
, ::picker(select)
und ::checkmark
ansehen.
Schauen wir uns all die oben genannten Features in Aktion an, indem wir das Beispiel durchgehen, das oben auf der Seite gezeigt wird.
Anpassbares select
-Markup
Unser Beispiel ist ein typisches <select>
-Menü, das Ihnen ermöglicht, ein Haustier auszuwählen. Das Markup ist wie folgt:
<form>
<p>
<label for="pet-select">Select pet:</label>
<select id="pet-select">
<button>
<selectedcontent></selectedcontent>
</button>
<option value="">Please select a pet</option>
<option value="cat">
<span class="icon" aria-hidden="true">🐱</span
><span class="option-label">Cat</span>
</option>
<option value="dog">
<span class="icon" aria-hidden="true">🐶</span
><span class="option-label">Dog</span>
</option>
<option value="hamster">
<span class="icon" aria-hidden="true">🐹</span
><span class="option-label">Hamster</span>
</option>
<option value="chicken">
<span class="icon" aria-hidden="true">🐔</span
><span class="option-label">Chicken</span>
</option>
<option value="fish">
<span class="icon" aria-hidden="true">🐟</span
><span class="option-label">Fish</span>
</option>
<option value="snake">
<span class="icon" aria-hidden="true">🐍</span
><span class="option-label">Snake</span>
</option>
</select>
</p>
</form>
Hinweis:
Das Attribut aria-hidden="true"
ist bei den Symbolen enthalten, so dass sie vor unterstützenden Technologien versteckt werden und die Optionswerte nicht doppelt angesagt werden (zum Beispiel "Katze Katze").
Das Beispiel-Markup ist fast identisch mit dem "klassischen" <select>
-Markup, mit folgenden Unterschieden:
-
Die Struktur
<button><selectedcontent></selectedcontent></button>
repräsentiert den Select-<button>
. Das Hinzufügen des<selectedcontent>
-Elements veranlasst den Browser, das aktuell ausgewählte<option>
innerhalb des Buttons zu klonen, das Sie dann mit benutzerdefinierten Stilen versehen können. Wenn diese Struktur nicht in Ihrem Markup enthalten ist, fällt der Browser auf das Rendern des ausgewählten Options-Textes im Standard-Button zurück, sodass Sie ihn nicht so leicht gestalten können.Hinweis: Sie können beliebige Inhalte innerhalb des
<button>
einfügen, um innerhalb des geschlossenen<select>
das darzustellen, was Sie möchten, aber seien Sie vorsichtig dabei. Was Sie einschließen, kann den zugänglichen Wert verändern, der unterstützender Technologie für das<select>
-Element ausgesetzt ist. -
Der Rest des
<select>
-Inhalts repräsentiert den Dropdown-Picker, der normalerweise auf die<option>
-Elemente beschränkt ist, die die verschiedenen Auswahlmöglichkeiten im Picker darstellen. Sie können anderen Inhalt im Picker einfügen, aber es wird nicht empfohlen. -
Traditionell konnten
<option>
-Elemente nur Text enthalten, aber in einem anpassbaren Select können Sie andere Markup-Strukturen wie Bilder, andere nicht-interaktive textstufenübergreifende semantische Elemente und mehr einfügen. Sie können sogar die::before
und::after
-Pseudo-Elemente verwenden, um weiteren Inhalt einzuschließen, obwohl Sie bedenken sollten, dass dies nicht in den übermittelbaren Wert eingeschlossen wird. In unserem Beispiel enthält jedes<option>
zwei<span>
-Elemente, die jeweils ein Symbol und eine Textbeschriftung enthalten, so dass jedes Styliert und unabhängig positioniert werden kann.Hinweis: Da der
<option>
-Inhalt mehrstufige DOM-Subbäume enthalten kann und nicht nur Textknoten, gibt es Regeln dafür, wie der Browser den aktuellen<select>
-Wert über JavaScript extrahieren sollte. Der Wert dertextContent
Eigenschaft des ausgewählten<option>
-Elements wird abgerufen,trim()
wird darauf ausgeführt, und das Ergebnis wird als<select>
-Wert festgelegt.
Dieses Design ermöglicht es nicht unterstützenden Browsern, auf eine klassische <select>
-Erfahrung zurückzufallen. Die Struktur <button><selectedcontent></selectedcontent></button>
wird komplett ignoriert, und der nicht-textuelle <option>
-Inhalt wird entfernt, um nur die Textknoten-Inhalte zurückzulassen, aber das Ergebnis wird immer noch funktionieren.
Opt-in zum benutzerdefinierten Select-Rendering
Um sich in die benutzerdefinierte Select-Funktionalität und minimale Browser-Grundstile einzuwählen (und die vom Betriebssystem bereitgestellte Gestaltung zu entfernen), müssen Ihr <select>
-Element und sein Dropdown-Picker (repräsentiert durch das ::picker(select)
-Pseudo-Element) beide einen appearance
-Wert von base-select
gesetzt haben:
select,
::picker(select) {
appearance: base-select;
}
Sie können wählen, nur das <select>
-Element in die neue Funktionalität einzuwählen und den Picker mit dem Standardbetriebssystemstil zu belassen, aber in den meisten Fällen werden Sie beides einwählen wollen. Sie können den Picker nicht einwählen, ohne das <select>
-Element einzuwählen.
Sobald dies geschehen ist, ergibt sich eine sehr schlichte Darstellung eines <select>
-Elements:
Sie können dies nun nach Belieben gestalten. Zunächst hat das <select>
-Element benutzerdefinierte border
, background
-Werte (die sich bei :hover
oder :focus
ändern) und padding
-Werte gesetzt, plus ein transition
, sodass die Hintergrundänderung sanft animiert wird:
select {
border: 2px solid #ddd;
background: #eee;
padding: 10px;
transition: 0.4s;
}
select:hover,
select:focus {
background: #ddd;
}
Gestaltung des Picker-Symbols
Um das Symbol innerhalb des Select-Buttons zu gestalten – den Pfeil, der nach unten zeigt, wenn das Select geschlossen ist – können Sie es mit dem ::picker-icon
-Pseudo-Element ansprechen. Der folgende Code gibt dem Symbol eine benutzerdefinierte color
und eine transition
, sodass Änderungen an seiner rotate
-Eigenschaft sanft animiert werden:
select::picker-icon {
color: #999;
transition: 0.4s rotate;
}
Als Nächstes wird ::picker-icon
mit der :open
-Pseudo-Klasse kombiniert – die den Select-Button nur dann anvisiert, wenn der Dropdown-Picker geöffnet ist –, um dem Symbol einen rotate
-Wert von 180deg
zu geben, wenn das <select>
geöffnet ist.
select:open::picker-icon {
rotate: 180deg;
}
Schauen wir uns die bisherige Arbeit an – achten Sie darauf, wie der Picker-Pfeil sanft um 180 Grad dreht, wenn das <select>
geöffnet und geschlossen wird:
Gestaltung des Dropdown-Pickers
Der Dropdown-Picker kann mit dem ::picker(select)
-Pseudo-Element angesprochen werden. Wie bereits erwähnt, enthält der Picker alles innerhalb des <select>
-Elements, das nicht der Button und das <selectedcontent>
ist. In unserem Beispiel bedeutet dies alle <option>
-Elemente und deren Inhalt.
Zuerst wird der standardmäßige schwarze border
des Pickers entfernt:
::picker(select) {
border: none;
}
Nun werden die <option>
-Elemente gestaltet. Sie werden mit flexbox angeordnet, alle zu Beginn des Flex-Containers ausgerichtet und inklusive eines 20px
gap
zwischen jedem. Jedes <option>
erhält ebenfalls die gleiche border
, background
, padding
und transition
wie das <select>
, um ein konsistentes Erscheinungsbild zu bieten:
option {
display: flex;
justify-content: flex-start;
gap: 20px;
border: 2px solid #ddd;
background: #eee;
padding: 10px;
transition: 0.4s;
}
Hinweis:
Anpassbare <select>
-Element-<option>
haben standardmäßig display: flex
auf sich gesetzt, aber dies ist trotzdem in unserem Stylesheet enthalten, um zu verdeutlichen, was vor sich geht.
Als Nächstes wird eine Kombination der :first-of-type
, :last-of-type
und :not()
-Pseudo-Klassen verwendet, um einen geeigneten border-radius
an den oberen und unteren Ecken des Pickers festzulegen und die border-bottom
von allen <option>
-Elementen außer dem letzten zu entfernen, damit die Ränder nicht unordentlich und doppelt aussehen:
option:first-of-type {
border-radius: 8px 8px 0 0;
}
option:last-of-type {
border-radius: 0 0 8px 8px;
}
option:not(option:last-of-type) {
border-bottom: none;
}
Danach wird eine andere background
-Farbe auf den ungeradzahligen <option>
-Elementen mit :nth-of-type(odd)
festgelegt, um Zebra-Stripes zu implementieren, und eine andere background
-Farbe wird bei den fokussierten und gehobenen <option>
-Elementen gesetzt, um eine hilfreiche visuelle Hervorhebung während der Auswahl zu bieten:
option:nth-of-type(odd) {
background: #fff;
}
option:hover,
option:focus {
background: plum;
}
Abschließend wird für diese Sektion eine größere font-size
für die <option>
-Symbole festgelegt (die sich innerhalb von <span>
-Elementen mit einer Klasse „icon“ befinden), um sie größer zu machen, und die text-box
-Eigenschaft wird verwendet, um etwas von dem lästigen Abstand an den Block-Start und Block-End-Kanten der Icon-Emojis zu entfernen, sodass sie sich besser mit den Textbeschriftungen ausrichten:
option .icon {
font-size: 1.6rem;
text-box: trim-both cap alphabetic;
}
Unser Beispiel wird jetzt folgendermaßen dargestellt:
Anpassung der Gestaltung des ausgewählten Optionsinhalt im Select-Button
Wenn Sie in den letzten paar Live-Beispielen eine Haustieroption auswählen, werden Sie ein Problem bemerken – die Haustiervarianten vergrößern die Höhe des Select-Buttons, was auch die Position des Picker-Symbols verändert, und es gibt keine Abstände zwischen dem Optionssymbol und der Beschriftung.
Das kann durch Ausblenden des Symbols behoben werden, wenn es sich im <selectedcontent>
befindet, das den Inhalt des ausgewählten <option>
so darstellt, wie er im Select-Button angezeigt wird. In unserem Beispiel wird es mithilfe von display: none
verborgen:
selectedcontent .icon {
display: none;
}
Dies beeinträchtigt nicht die Gestaltung des <option>
-Inhalts, wie er im Dropdown-Picker erscheint.
Gestaltung der aktuell gewählten Option
Um die aktuell ausgewählte <option>
zu gestalten, wie sie im Dropdown-Picker erscheint, können Sie sie mithilfe der :checked
-Pseudo-Klasse anvisieren. Dies wird verwendet, um die font-weight
der ausgewählten <option>
fett zu setzen:
option:checked {
font-weight: bold;
}
Gestaltung des aktuellen Auswahl-Häkchens
Sie haben wahrscheinlich bemerkt, dass, wenn Sie den Picker öffnen, um eine Auswahl zu treffen, die aktuell ausgewählte <option>
ein Häkchen am Beginn aufweist. Dieses Häkchen kann mit dem ::checkmark
-Pseudo-Element angesprochen werden. Zum Beispiel, um dieses Häkchen zu verbergen (zum Beispiel via display: none
).
Sie können sich auch entscheiden, etwas Interessanteres damit zu tun – früher wurden die <option>
-Elemente horizontal mit Flexbox angeordnet und die Flex-Items zum Beginn der Reihe ausgerichtet. Im folgenden Regel wird das Häkchen vom Start der Reihe ans Ende bewegt, indem ihm ein order
-Wert größer als 0
zugewiesen wird und es mit einem auto
margin-left
Wert ausgerichtet wird (siehe Ausrichtung und automatische Ränder).
Abschließend wird der Wert der content
Eigenschaft auf ein anderes Emoji gesetzt, um ein anderes Symbol zur Anzeige zu setzen.
option::checkmark {
order: 1;
margin-left: auto;
content: "☑️";
}
Hinweis:
Die ::checkmark
und ::picker-icon
Pseudo-Elemente sind nicht im Accessibility-Baum enthalten, sodass jeder generierte content
, der darauf gesetzt wird, nicht von unterstützenden Technologien angesagt wird. Sie sollten jedoch immer noch sicherstellen, dass jedes neue Symbol, das Sie setzen, visuell für seinen vorgesehenen Zweck Sinn ergibt.
Lassen Sie uns erneut kontrollieren, wie das Beispiel rendert. Der aktualisierte Zustand nach den letzten drei Abschnitten ist wie folgt:
Animation des Pickers mit Popover-Zuständen
Das anpassbare <select>
-Element-Select-button
und der Dropdown-Picker erhalten automatisch eine Aufrufer-/Popover-Beziehung, wie im Abschnitt Verwendung der Popover API beschrieben. Es gibt viele Vorteile, die dies den <select>
-Elementen bringt; unser Beispiel nutzt die Übergänge zwischen verborgenen und angezeigten Popover-Zuständen, um Animationen zu ermöglichen. Die :popover-open
-Pseudo-Klasse repräsentiert Popover im angezeigten Zustand.
Die Technik wird in diesem Abschnitt schnell behandelt – lesen Sie Animating Popovers für eine ausführlichere Beschreibung.
Zuerst wird der Picker mit ::picker(select)
ausgewählt und erhält einen opacity
Wert von 0
und einen transition
-Wert von all 0.4s allow-discrete
. Dies führt dazu, dass alle Eigenschaften, die sich ändern, wenn der Popover-Zustand von verborgen zu angezeigt wechselt, animiert werden.
::picker(select) {
opacity: 0;
transition: all 0.4s allow-discrete;
}
Die Liste der übergangenen Eigenschaften enthält opacity
, aber es gibt auch zwei diskrete Eigenschaften, deren Werte durch die Standardstile des Browsers gesetzt werden:
display
-
Die
display
-Werte ändern sich vonnone
zublock
, wenn der Popover-Zustand von verborgen zu angezeigt wechselt. Diese muss animiert werden, um sicherzustellen, dass andere Übergänge sichtbar sind. overlay
-
Der
overlay
-Wert ändert sich vonnone
zuauto
, wenn der Popover-Zustand von verborgen zu angezeigt wechselt, um es auf die oberste Schicht zu befördern, und zurück, wenn es verborgen ist, um es zu entfernen. Diese muss animiert werden, um sicherzustellen, dass das Entfernen des Popovers von der obersten Schicht aufgeschoben wird, bis der Übergang abgeschlossen ist, um sicherzustellen, dass der Übergang sichtbar ist.
Hinweis:
Der Wert allow-discrete
wird benötigt, um diskrete Eigenschaftsanimationen zu ermöglichen.
Als Nächstes wird der Picker im angezeigten Zustand mit ::picker(select):popover-open
ausgewählt und erhält einen opacity
-Wert von 1
– dies ist der Endzustand des Übergangs:
::picker(select):popover-open {
opacity: 1;
}
Schließlich, da der Picker übergangen wird, während er sich von display: none
zu einem display
-Wert bewegt, der ihn sichtbar macht, muss der Ausgangszustand des Übergangs in einem @starting-style
-Block spezifiziert werden:
@starting-style {
::picker(select):popover-open {
opacity: 0;
}
}
Diese Regeln arbeiten zusammen, um den Picker sanft ein- und ausblenden zu lassen, wenn das <select>
geöffnet und geschlossen wird.
Positionierung des Pickers mittels Anker-Positionierung
Ein anpassbares <select>
-Element-Select-Button und Dropdown-Picker haben einen impliziten Ankerbezug, und der Picker ist automatisch über die CSS-Ankerpositionierung mit dem Select-Button verbunden. Dies bedeutet, dass keine explizite Verbindung mit den anchor-name
und position-anchor
-Eigenschaften hergestellt werden muss.
Darüber hinaus bieten die Standardstile des Browsers eine Standardposition, die Sie anpassen können, wie im Abschnitt Positionierung von Elementen relativ zu ihrem Anker erklärt.
In unserer Demo wird die Position des Pickers relativ zu seinem Anker durch die Verwendung der anchor()
-Funktion innerhalb seiner top
und left
-Eigenschaftswerte eingestellt:
::picker(select) {
top: calc(anchor(bottom) + 1px);
left: anchor(10%);
}
Dies führt dazu, dass die obere Kante des Pickers immer 1 Pixel unterhalb der unteren Kante des Select-Buttons und die linke Kante des Pickers immer 10%
der Breite des Select-Buttons von seiner linken Kante aus positioniert wird.
Endergebnis
Nach den letzten beiden Abschnitten wird der endgültige aktualisierte Zustand unseres <select>
wie folgt gerendert:
Anpassung anderer klassischer Select-Merkmale
Die obigen Abschnitte haben alle neuen Funktionalitäten in anpassbaren Selects behandelt und gezeigt, wie sie sowohl mit klassischen einzeiligen Selects als auch mit verwandten modernen Features wie Popovers und Ankerpositionierung interagieren. Es gibt einige andere <select>
-Element-Features, die bisher nicht erwähnt wurden; dieser Abschnitt spricht darüber, wie sie derzeit mit anpassbaren Selects funktionieren:
<select multiple>
-
Es gibt derzeit keine spezifizierte Unterstützung für das
multiple
-Attribut auf anpassbaren<select>
-Elementen, aber daran wird in der Zukunft gearbeitet. <optgroup>
-
Der Standardstil der
<optgroup>
-Elemente ist derselbe wie in klassischen<select>
-Elementen – fett und weniger eingezogen als die enthaltenen Optionen. Sie müssen sicherstellen, dass die<optgroup>
-Elemente zur Gesamtgestaltung passen und daran denken, dass sie sich so verhalten, wie es von Containern in konventionellem HTML erwartet wird. In anpassbaren<select>
-Elementen ist das<legend>
-Element als Kind von<optgroup>
erlaubt, um ein leicht anvisierbares und ansprechendes Label bereitzustellen. Dies ersetzt jeglichen Text, der imlabel
-Attribut des<optgroup>
-Elements eingetragen ist, und hat die gleichen Semantiken.
Als Nächstes
Im nächsten Artikel dieses Moduls werden wir die verschiedenen UI-Pseudoklassen erkunden, die uns in modernen Browsern zur Verfügung stehen, um Formulare in verschiedenen Zuständen zu gestalten.