Ein grundlegender Überblick über die Erstellung einer responsiven und barrierefreien Schalterkomponente.
In diesem Beitrag möchte ich meine Gedanken zu einer Möglichkeit zum Erstellen von Schalterkomponenten teilen. Demo ansehen.
Wenn du lieber ein Video ansiehst, findest du hier eine YouTube-Version dieses Beitrags:
Übersicht
Ein Schalter funktioniert ähnlich wie ein Kästchen, stellt aber explizit boolesche Ein- und Aus-Zustände dar.
In dieser Demo wird für den Großteil der Funktionalität <input type="checkbox" role="switch">
verwendet. Dies hat den Vorteil, dass kein CSS oder JavaScript erforderlich ist, um voll funktionsfähig und zugänglich zu sein. Durch das Laden von CSS wird die Unterstützung von Sprachen, die von rechts nach links geschrieben werden, vertikaler Ausrichtung und Animationen ermöglicht. Wenn Sie JavaScript laden, wird der Schalter beweglich und greifbar.
Benutzerdefinierte Eigenschaften
Die folgenden Variablen stehen für die verschiedenen Teile des Schalters und ihre Optionen. Als oberste Klasse enthält .gui-switch
benutzerdefinierte Eigenschaften, die in allen untergeordneten Komponenten verwendet werden, sowie Einstiegspunkte für die zentrale Anpassung.
Verfolgen
Die Länge (--track-size
), der Abstand und zwei Farben:
.gui-switch {
--track-size: calc(var(--thumb-size) * 2);
--track-padding: 2px;
--track-inactive: hsl(80 0% 80%);
--track-active: hsl(80 60% 45%);
--track-color-inactive: var(--track-inactive);
--track-color-active: var(--track-active);
@media (prefers-color-scheme: dark) {
--track-inactive: hsl(80 0% 35%);
--track-active: hsl(80 60% 60%);
}
}
Thumbnails
Größe, Hintergrundfarbe und Farben für Interaktions-Highlights:
.gui-switch {
--thumb-size: 2rem;
--thumb: hsl(0 0% 100%);
--thumb-highlight: hsl(0 0% 0% / 25%);
--thumb-color: var(--thumb);
--thumb-color-highlight: var(--thumb-highlight);
@media (prefers-color-scheme: dark) {
--thumb: hsl(0 0% 5%);
--thumb-highlight: hsl(0 0% 100% / 25%);
}
}
Weniger Bewegung
Um einen eindeutigen Alias hinzuzufügen und Wiederholungen zu vermeiden, kann eine Media-Abfrage für Nutzer mit reduzierter Bewegungspräferenz mit dem PostCSS-Plug-in in eine benutzerdefinierte Property eingefügt werden. Dabei wird diese Vorlage für Media-Abfragen Version 5 verwendet:
@custom-media --motionOK (prefers-reduced-motion: no-preference);
Markieren & Zeichnen
Ich habe mein <input type="checkbox" role="switch">
-Element in ein <label>
-Element gewickelt, um ihre Beziehung zu bündeln und Unklarheiten bei der Verknüpfung von Kästchen und Label zu vermeiden. Gleichzeitig haben Nutzer die Möglichkeit, mit dem Label zu interagieren, um die Eingabe zu aktivieren oder zu deaktivieren.
<label for="switch" class="gui-switch">
Label text
<input type="checkbox" role="switch" id="switch">
</label>
<input type="checkbox">
ist bereits mit einer API und einem Status ausgestattet. Der Browser verwaltet das checked
-Attribut und Eingabeereignisse wie oninput
und onchanged
.
Layouts
Flexbox-, Grid- und benutzerdefinierte Eigenschaften sind entscheidend für die Verwaltung der Stile dieser Komponente. Sie zentralisieren Werte, geben ansonsten mehrdeutigen Berechnungen oder Bereichen Namen und ermöglichen eine kleine API für benutzerdefinierte Properties, um Komponenten ganz einfach anzupassen.
.gui-switch
Das Layout der obersten Ebene für den Schalter ist Flexbox. Die Klasse .gui-switch
enthält die privaten und öffentlichen benutzerdefinierten Properties, die die untergeordneten Elemente zum Berechnen ihrer Layouts verwenden.
.gui-switch {
display: flex;
align-items: center;
gap: 2ch;
justify-content: space-between;
}
Das Flexbox-Layout kann wie jedes andere Flexbox-Layout erweitert und geändert werden.
So platzieren Sie beispielsweise Labels über oder unter einem Schalter oder ändern die flex-direction
:
<label for="light-switch" class="gui-switch" style="flex-direction: column">
Default
<input type="checkbox" role="switch" id="light-switch">
</label>
Verfolgen
Die Kästcheneingabe wird wie eine Schaltspur gestaltet, indem die normale appearance: checkbox
entfernt und stattdessen eine eigene Größe angegeben wird:
.gui-switch > input {
appearance: none;
inline-size: var(--track-size);
block-size: var(--thumb-size);
padding: var(--track-padding);
flex-shrink: 0;
display: grid;
align-items: center;
grid: [track] 1fr / [track] 1fr;
}
Außerdem wird ein Rasterbereich mit einer einzelnen Zelle erstellt, den ein Nutzer beanspruchen kann.
Thumbnails
Mit dem Stil appearance: none
wird auch das vom Browser bereitgestellte visuelle Häkchen entfernt. Diese Komponente verwendet ein Pseudo-Element und die :checked
-Pseudoklasse für die Eingabe, um diesen visuellen Indikator zu ersetzen.
Der Vorschaubereich ist ein untergeordnetes Pseudo-Element, das an input[type="checkbox"]
angehängt ist und sich über dem Titeltrack statt darunter stapelt, indem es den Rasterbereich track
beansprucht:
.gui-switch > input::before {
content: "";
grid-area: track;
inline-size: var(--thumb-size);
block-size: var(--thumb-size);
}
Stile
Mit benutzerdefinierten Eigenschaften können Sie eine vielseitige Schalterkomponente erstellen, die sich an Farbschemata, von rechts nach links geschriebene Sprachen und Bewegungseinstellungen anpasst.
Touch-Interaktionsstile
Auf Mobilgeräten fügen Browser Labels und Eingaben Tippeffekte und Textauswahlfunktionen hinzu. Dies wirkte sich negativ auf den Stil und das visuelle Interaktionsfeedback aus, das für diese Umstellung erforderlich war. Mit ein paar Zeilen CSS kann ich diese Effekte entfernen und meinen eigenen cursor: pointer
-Stil hinzufügen:
.gui-switch {
cursor: pointer;
user-select: none;
-webkit-tap-highlight-color: transparent;
}
Es ist nicht immer ratsam, diese Stile zu entfernen, da sie wertvolles visuelles Interaktionsfeedback sein können. Wenn Sie sie entfernen, müssen Sie benutzerdefinierte Alternativen angeben.
Verfolgen
Die Stile dieses Elements beziehen sich hauptsächlich auf seine Form und Farbe, auf die es über die Kaskade vom übergeordneten Element .gui-switch
zugreift.
.gui-switch > input {
appearance: none;
border: none;
outline-offset: 5px;
box-sizing: content-box;
padding: var(--track-padding);
background: var(--track-color-inactive);
inline-size: var(--track-size);
block-size: var(--thumb-size);
border-radius: var(--track-size);
}
Vier benutzerdefinierte Properties bieten eine Vielzahl von Anpassungsoptionen für den Schalter-Track. border: none
wird hinzugefügt, da appearance: none
die Rahmen nicht in allen Browsern aus dem Kästchen entfernt.
Thumbnails
Das Vorschauelement befindet sich bereits rechts track
, benötigt aber Kreisstile:
.gui-switch > input::before {
background: var(--thumb-color);
border-radius: 50%;
}
Interaktion
Mit benutzerdefinierten Properties können Sie sich auf Interaktionen vorbereiten, bei denen Highlights beim Hovern und Änderungen der Position des Schiebereglers angezeigt werden. Außerdem wird die Einstellung des Nutzers geprüft, bevor die Stilvorlage für die Bewegung oder die hervorgehobenen Elemente beim Hovering geändert wird.
.gui-switch > input::before {
box-shadow: 0 0 0 var(--highlight-size) var(--thumb-color-highlight);
@media (--motionOK) { & {
transition:
transform var(--thumb-transition-duration) ease,
box-shadow .25s ease;
}}
}
Daumenposition
Benutzerdefinierte Properties bieten einen einzigen Mechanismus zur Positionierung des Schiebereglers im Titel. Es stehen die Track- und Thumbnail-Größen zur Verfügung, die wir in Berechnungen verwenden, damit der Daumen-Versatz innerhalb des Tracks korrekt verschoben wird: 0%
und 100%
.
Das input
-Element hat die Positionierungsvariable --thumb-position
und das Pseudo-Element „thumb“ verwendet sie als translateX
-Position:
.gui-switch > input {
--thumb-position: 0%;
}
.gui-switch > input::before {
transform: translateX(var(--thumb-position));
}
Wir können --thumb-position
jetzt über CSS und die Pseudoklassen für Kästchenelemente ändern. Da wir transition: transform
var(--thumb-transition-duration) ease
zuvor bedingt für dieses Element festgelegt haben, können diese Änderungen animiert werden, wenn sie geändert werden:
/* positioned at the end of the track: track length - 100% (thumb width) */
.gui-switch > input:checked {
--thumb-position: calc(var(--track-size) - 100%);
}
/* positioned in the center of the track: half the track - half the thumb */
.gui-switch > input:indeterminate {
--thumb-position: calc(
(var(--track-size) / 2) - (var(--thumb-size) / 2)
);
}
Ich fand, dass diese entkoppelte Orchestrierung gut funktioniert hat. Das thumb-Element bezieht sich nur auf einen Stil, eine translateX
-Position. Die Eingabe kann alle Komplexität und Berechnungen verwalten.
Branche
Die Unterstützung wurde mit der Modifikatorklasse -vertical
durchgeführt, die eine Rotation mit CSS-Transformationen zum input
-Element hinzufügt.
Durch ein in 3D gedrehtes Element ändert sich jedoch nicht die Gesamthöhe der Komponente, was das Blocklayout beeinträchtigen kann. Berücksichtigen Sie dies mit den Variablen --track-size
und --track-padding
. Berechnen Sie den Mindestabstand, der erforderlich ist, damit eine vertikale Schaltfläche im Layout wie erwartet dargestellt wird:
.gui-switch.-vertical {
min-block-size: calc(var(--track-size) + calc(var(--track-padding) * 2));
& > input {
transform: rotate(-90deg);
}
}
(RTL) Linksläufig
Zusammen mit einem CSS-Freund, Elad Schecter, habe ich einen Prototyp für ein ausziehbares Seitenmenü mit CSS-Transformierungen erstellt, das Sprachen von rechts nach links unterstützt, indem eine einzelne Variable umgedreht wurde. Wir haben dies getan, weil es in CSS keine logischen Attributtransformationen gibt und es dies vielleicht nie gibt. Elad hatte die gute Idee, einen benutzerdefinierten Property-Wert zu verwenden, um Prozentsätze umzukehren, damit unsere benutzerdefinierte Logik für logische Transformationen an einem einzigen Ort verwaltet werden kann. Ich habe bei diesem Wechsel dieselbe Technik verwendet und es hat gut funktioniert:
.gui-switch {
--isLTR: 1;
&:dir(rtl) {
--isLTR: -1;
}
}
Eine benutzerdefinierte Eigenschaft namens --isLTR
hat anfangs den Wert 1
, was true
bedeutet, da unser Layout standardmäßig von links nach rechts ausgerichtet ist. Mit dem CSS-Pseudoklass :dir()
wird der Wert dann auf -1
gesetzt, wenn sich die Komponente in einem Layout von rechts nach links befindet.
Verwenden Sie --isLTR
in einer calc()
innerhalb einer Transformation:
.gui-switch.-vertical > input {
transform: rotate(-90deg);
transform: rotate(calc(90deg * var(--isLTR) * -1));
}
Die Drehung des vertikalen Schalters berücksichtigt jetzt die gegenüberliegende Seitenposition, die für das Layout von rechts nach links erforderlich ist.
Die translateX
-Transformationen für das Daumen-Pseudoelement müssen ebenfalls aktualisiert werden, um die gegenüberliegende Anforderung zu berücksichtigen:
.gui-switch > input:checked {
--thumb-position: calc(var(--track-size) - 100%);
--thumb-position: calc((var(--track-size) - 100%) * var(--isLTR));
}
.gui-switch > input:indeterminate {
--thumb-position: calc(
(var(--track-size) / 2) - (var(--thumb-size) / 2)
);
--thumb-position: calc(
((var(--track-size) / 2) - (var(--thumb-size) / 2))
* var(--isLTR)
);
}
Mit diesem Ansatz lassen sich zwar nicht alle Anforderungen an ein Konzept wie logische CSS-Transformationen erfüllen, er bietet aber für viele Anwendungsfälle einige DRY-Prinzipien.
Bundesstaaten
Die Verwendung der integrierten input[type="checkbox"]
wäre nicht vollständig, ohne die verschiedenen Status zu berücksichtigen, in denen sie sich befinden kann: :checked
, :disabled
, :indeterminate
und :hover
. :focus
blieb absichtlich außen und wurde nur am Versatz angepasst. Der Fokusring sah in Firefox und Safari gut aus:
Geprüft
<label for="switch-checked" class="gui-switch">
Default
<input type="checkbox" role="switch" id="switch-checked" checked="true">
</label>
Dieser Status entspricht dem Status on
. In diesem Status ist der Hintergrund der Eingabe „track“ auf die aktive Farbe und die Position des Schiebereglers auf „Ende“ festgelegt.
.gui-switch > input:checked {
background: var(--track-color-active);
--thumb-position: calc((var(--track-size) - 100%) * var(--isLTR));
}
Deaktiviert
<label for="switch-disabled" class="gui-switch">
Default
<input type="checkbox" role="switch" id="switch-disabled" disabled="true">
</label>
Eine :disabled
-Schaltfläche sieht nicht nur optisch anders aus, sondern sollte das Element auch unveränderlich machen.Die Unveränderlichkeit von Interaktionen ist unabhängig vom Browser, aber die visuellen Status benötigen Stile, da appearance: none
verwendet wird.
.gui-switch > input:disabled {
cursor: not-allowed;
--thumb-color: transparent;
&::before {
cursor: not-allowed;
box-shadow: inset 0 0 0 2px hsl(0 0% 100% / 50%);
@media (prefers-color-scheme: dark) { & {
box-shadow: inset 0 0 0 2px hsl(0 0% 0% / 50%);
}}
}
}
Dieser Status ist schwierig, da er dunkle und helle Designs mit deaktiviertem und aktiviertem Status benötigt. Ich habe für diese Status minimalistische Stile ausgewählt, um die Wartungsbelastung der Stilkombinationen zu verringern.
Unklar
Ein häufig vergessener Status ist :indeterminate
, bei dem ein Kästchen weder aktiviert noch deaktiviert ist. Das ist ein unterhaltsamer Zustand, er ist einladend und unprätentiös. Eine gute Erinnerung daran, dass boolesche Status Zwischenstatus haben können.
Es ist schwierig, ein Kästchen auf „Unbestimmt“ zu setzen. Das geht nur mit JavaScript:
<label for="switch-indeterminate" class="gui-switch">
Indeterminate
<input type="checkbox" role="switch" id="switch-indeterminate">
<script>document.getElementById('switch-indeterminate').indeterminate = true</script>
</label>
Da der Bundesstaat für mich unscheinbar und einladend ist, erschien es mir angemessen, die Position des Schalters in der Mitte zu platzieren:
.gui-switch > input:indeterminate {
--thumb-position: calc(
calc(calc(var(--track-size) / 2) - calc(var(--thumb-size) / 2))
* var(--isLTR)
);
}
Mauszeiger hierher bewegen
Hover-Interaktionen sollten eine visuelle Unterstützung für die verbundene Benutzeroberfläche bieten und auch eine Richtung für die interaktive Benutzeroberfläche vorgeben. Mit diesem Schalter wird der Daumen als halbtransparentes Ring hervorgehoben, wenn der Mauszeiger auf das Label oder die Eingabe bewegt wird. Diese Hover-Animation weist dann auf das interaktive Vorschauelement hin.
Der Effekt „Hervorheben“ wird mit box-shadow
erstellt. Erhöhen Sie die Größe von --highlight-size
, wenn der Mauszeiger auf eine nicht deaktivierte Eingabe schwebt. Wenn der Nutzer mit Bewegung einverstanden ist, wird die box-shadow
animiert und vergrößert sich. Wenn er mit Bewegung nicht einverstanden ist, wird das Highlight sofort angezeigt:
.gui-switch > input::before {
box-shadow: 0 0 0 var(--highlight-size) var(--thumb-color-highlight);
@media (--motionOK) { & {
transition:
transform var(--thumb-transition-duration) ease,
box-shadow .25s ease;
}}
}
.gui-switch > input:not(:disabled):hover::before {
--highlight-size: .5rem;
}
JavaScript
Ich finde, dass ein Schalter, der versucht, eine physische Oberfläche zu emulieren, etwas unheimlich wirken kann, vor allem diese Art mit einem Kreis in einem Track. iOS hat das mit seinem Schalter richtig gemacht. Sie können ihn hin und her ziehen und es ist sehr zufriedenstellend, diese Option zu haben. Umgekehrt kann ein UI-Element inaktiv erscheinen, wenn ein Ziehen versucht wird und nichts passiert.
Ziehbare „Mag ich“-Bewertungen
Das Pseudo-Element „thumb“ erhält seine Position vom .gui-switch > input
-Element mit dem var(--thumb-position)
-Scope. JavaScript kann einen Inline-Stilwert für die Eingabe bereitstellen, um die Position des Schiebereglers dynamisch zu aktualisieren, sodass es so aussieht, als würde er der Mausbewegung folgen. Wenn der Mauszeiger losgelassen wird, entfernen Sie die Inline-Styles und ermitteln Sie mithilfe des benutzerdefinierten Attributs --thumb-position
, ob der Drag-Vorgang eher dem Aus- oder dem Ein-Status entsprach. Dies ist das Rückgrat der Lösung: Mithilfe von Mauszeiger-Ereignissen werden die Mauszeigerpositionen bedingt erfasst, um benutzerdefinierte CSS-Properties zu ändern.
Da die Komponente bereits zu 100 % funktionierte, bevor dieses Script angezeigt wurde, ist es ziemlich aufwendig, das vorhandene Verhalten beizubehalten, z. B. das Klicken auf ein Label, um die Eingabe zu aktivieren oder zu deaktivieren. Unser JavaScript sollte keine Funktionen hinzufügen, die auf Kosten bestehender Funktionen gehen.
touch-action
Ziehen ist eine Geste, eine benutzerdefinierte Geste, was sie zu einem guten Kandidaten für touch-action
-Vorteile macht. Bei diesem Schalter sollte unser Script eine horizontale Geste verarbeiten oder eine vertikale Geste für die vertikale Schaltervariante erfassen. Mit touch-action
können wir dem Browser mitteilen, welche Touch-Gesten für dieses Element verarbeitet werden sollen, damit ein Script eine Geste ohne Konkurrenz verarbeiten kann.
Im folgenden CSS-Code wird dem Browser mitgeteilt, dass vertikale Touch-Gesten verarbeitet werden sollen, wenn eine Touch-Geste innerhalb dieses Schalters beginnt, und horizontale Touch-Gesten ignoriert werden sollen:
.gui-switch > input {
touch-action: pan-y;
}
Das gewünschte Ergebnis ist eine horizontale Geste, die nicht auch die Seite schwenken oder scrollen lässt. Ein vertikaler Cursor kann innerhalb der Eingabe beginnen und die Seite scrollen, horizontale Cursor werden jedoch benutzerdefiniert verarbeitet.
Dienstprogramme für Pixelwerte
Bei der Einrichtung und während des Ziehens müssen verschiedene berechnete Zahlenwerte aus Elementen übernommen werden. Die folgenden JavaScript-Funktionen geben berechnete Pixelwerte für eine CSS-Eigenschaft zurück. Sie wird im Einrichtungsskript so verwendet: getStyle(checkbox, 'padding-left')
.
const getStyle = (element, prop) => {
return parseInt(window.getComputedStyle(element).getPropertyValue(prop));
}
const getPseudoStyle = (element, prop) => {
return parseInt(window.getComputedStyle(element, ':before').getPropertyValue(prop));
}
export {
getStyle,
getPseudoStyle,
}
Beachten Sie, dass window.getComputedStyle()
ein zweites Argument akzeptiert, ein Ziel-Pseudo-Element. Es ist ziemlich praktisch, dass JavaScript so viele Werte aus Elementen lesen kann, sogar aus Pseudoelementen.
dragging
Dies ist ein wichtiger Moment für die Drag-Logik. Im Funktions-Ereignis-Handler gibt es einige Dinge zu beachten:
const dragging = event => {
if (!state.activethumb) return
let {thumbsize, bounds, padding} = switches.get(state.activethumb.parentElement)
let directionality = getStyle(state.activethumb, '--isLTR')
let track = (directionality === -1)
? (state.activethumb.clientWidth * -1) + thumbsize + padding
: 0
let pos = Math.round(event.offsetX - thumbsize / 2)
if (pos < bounds.lower) pos = 0
if (pos > bounds.upper) pos = bounds.upper
state.activethumb.style.setProperty('--thumb-position', `${track + pos}px`)
}
Der Script-Held ist state.activethumb
, der kleine Kreis, den dieses Script zusammen mit einem Zeiger positioniert. Das switches
-Objekt ist ein Map()
, bei dem die Schlüssel .gui-switch
sind und die Werte gecachte Grenzen und Größen sind, die das Script effizient halten. Für die Ausrichtung von rechts nach links wird dieselbe benutzerdefinierte CSS-Eigenschaft verwendet wie für --isLTR
. Damit kann die Logik umgekehrt und die Unterstützung für RTL fortgesetzt werden. Auch event.offsetX
ist wertvoll, da es einen Deltawert enthält, der für die Positionierung des Schiebereglers nützlich ist.
state.activethumb.style.setProperty('--thumb-position', `${track + pos}px`)
Mit dieser letzten CSS-Zeile wird die benutzerdefinierte Property festgelegt, die vom „thumb“-Element verwendet wird. Andernfalls würde diese Wertzuweisung im Laufe der Zeit übergangen, aber durch ein früheres Zeigerereignis wurde --thumb-transition-duration
vorübergehend auf 0s
gesetzt. Dadurch wird eine langsame Interaktion entfernt.
dragEnd
Damit der Nutzer weit außerhalb des Schalters ziehen und loslassen kann, musste ein globales Fensterereignis registriert werden:
window.addEventListener('pointerup', event => {
if (!state.activethumb) return
dragEnd(event)
})
Ich denke, es ist sehr wichtig, dass die Nutzenden frei ziehen können und die Benutzeroberfläche so intelligent ist, dass sie dies berücksichtigt. Die Umstellung erforderte nicht viel, musste aber während des Entwicklungsprozesses sorgfältig berücksichtigt werden.
const dragEnd = event => {
if (!state.activethumb) return
state.activethumb.checked = determineChecked()
if (state.activethumb.indeterminate)
state.activethumb.indeterminate = false
state.activethumb.style.removeProperty('--thumb-transition-duration')
state.activethumb.style.removeProperty('--thumb-position')
state.activethumb.removeEventListener('pointermove', dragging)
state.activethumb = null
padRelease()
}
Die Interaktion mit dem Element ist abgeschlossen. Jetzt muss die Eigenschaft „Eingabe“ aktiviert und alle Gestenereignisse entfernt werden. Das Kästchen wird durch state.activethumb.checked = determineChecked()
ersetzt.
determineChecked()
Diese Funktion, die von dragEnd
aufgerufen wird, bestimmt, wo sich der aktuelle Zeiger innerhalb der Grenzen seines Tracks befindet, und gibt „wahr“ zurück, wenn er mindestens 50 % des Tracks zurückgelegt hat:
const determineChecked = () => {
let {bounds} = switches.get(state.activethumb.parentElement)
let curpos =
Math.abs(
parseInt(
state.activethumb.style.getPropertyValue('--thumb-position')))
if (!curpos) {
curpos = state.activethumb.checked
? bounds.lower
: bounds.upper
}
return curpos >= bounds.middle
}
Weitere Überlegungen
Aufgrund der gewählten ursprünglichen HTML-Struktur war für die Drag-Geste etwas Code-Schulden erforderlich, insbesondere die Einbettung der Eingabe in ein Label. Da das Label ein übergeordnetes Element ist, erhält es nach der Eingabe Klickinteraktionen. Am Ende des dragEnd
-Ereignisses haben Sie vielleicht die merkwürdig klingende Funktion padRelease()
bemerkt.
const padRelease = () => {
state.recentlyDragged = true
setTimeout(_ => {
state.recentlyDragged = false
}, 300)
}
So wird berücksichtigt, dass das Label diesen Klick später erhält, da die Interaktion eines Nutzers dadurch deaktiviert oder aktiviert wird.
Wenn ich das noch einmal machen würde, würde ich möglicherweise das DOM während der UX-Optimierung mit JavaScript anpassen, um ein Element zu erstellen, das Labelklicks selbst verarbeitet und nicht mit dem integrierten Verhalten in Konflikt steht.
Diese Art von JavaScript ist meine am wenigsten bevorzugte, da ich kein bedingtes Ereignis-Bubbling verwalten möchte:
const preventBubbles = event => {
if (state.recentlyDragged)
event.preventDefault() && event.stopPropagation()
}
Fazit
Diese winzige Schalterkomponente war am Ende die größte Herausforderung aller GUI-Herausforderungen! Wie würden Sie es machen?
Lassen Sie uns unsere Ansätze diversifizieren und alle Möglichkeiten kennenlernen, wie Sie im Web entwickeln können. Erstelle eine Demo und sende Tweets an mich. Ich füge sie dann unten im Abschnitt zu Community-Remixen hinzu.
Remixe der Community
- @KonstantinRouda mit einem benutzerdefinierten Element: Demo und Code.
- @jhvanderschee mit einer Schaltfläche: Codepen.
Ressourcen
Suchen Sie den .gui-switch
-Quellcode auf GitHub.