Tworzenie komponentu przełącznika

Podstawowe informacje o tym, jak tworzyć elastyczny i dostępny komponent przełącznika.

W tym poście chcę podzielić się z Wami sposobem na tworzenie komponentów przełączników. Wypróbuj wersję demonstracyjną.

Prezentacja

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

Omówienie

Przełącznik działa podobnie jak pole wyboru, ale wyraźnie reprezentuje stany włączone i wyłączone.

W tym pokazie większość funkcji jest realizowana za pomocą <input type="checkbox" role="switch">, co ma tę zaletę, że nie wymaga korzystania z CSS ani JavaScriptu, aby zapewnić pełną funkcjonalność i dostępność. Wczytywanie CSS umożliwia obsługę języków zapisywanych od prawej do lewej, animacji i wielu innych funkcji. Po załadowaniu JavaScriptu przełącznik staje się możliwy do przeciągnięcia i dotykowy.

Właściwości niestandardowe

Podane niżej zmienne reprezentują różne części przełącznika i ich opcje. Klasa najwyższego poziomu .gui-switch zawiera właściwości niestandardowe używane w podrzędnych komponentach oraz punkty wejścia do scentralizowanej personalizacji.

Monitoruj

Długość (--track-size), dopełnienie i 2 kolory:

.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%);
  }
}

Miniatura

rozmiar, kolor tła i kolory zaznaczenia interakcji:

.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%);
  }
}

Ograniczony ruch

Aby dodać czytelny alias i zmniejszyć powtarzanie, możesz umieścić w usłudze niestandardowej za pomocą wtyczki PostCSS żądanie dotyczące multimediów z uwzględnieniem preferencji użytkownika dotyczących ograniczonego ruchu na podstawie tej specyfikacji w wersji roboczej w zapytaniach dotyczących multimediów 5:

@custom-media --motionOK (prefers-reduced-motion: no-preference);

Znacznik

Element <input type="checkbox" role="switch"> został zawinięty w element <label>, aby powiązać je ze sobą i uniknąć niejasności w powiązaniu pola wyboru z etykietą, a jednocześnie umożliwić użytkownikowi interakcję z etykietą w celu przełączania danych wejściowych.

Etykieta i pole wyboru bez stylizacji.

<label for="switch" class="gui-switch">
  Label text
  <input type="checkbox" role="switch" id="switch">
</label>

<input type="checkbox"> ma gotowy interfejs API i stan. Przeglądarka zarządza właściwością checkedzdarzeniami wprowadzania, takimi jak oninputonchanged.

Układy

Flexbox, grid i właściwości niestandardowe są kluczowe dla zachowania stylów tego komponentu. Umożliwiają one centralizację wartości, nadawanie nazw niejednoznacznym obliczeniom lub obszarom oraz zapewniają dostęp do małego interfejsu API usługi niestandardowej, która ułatwia dostosowywanie komponentów.

.gui-switch

Układ najwyższego poziomu przełącznika to flexbox. Klasa .gui-switch zawiera prywatne i publiczne właściwości niestandardowe, których dzieci używają do obliczania swoich układów.

Nakładka narzędzi dewelopera Flexbox na etykietę poziomą i przełącznik, pokazująca ich układ i rozmieszczenie w przestrzeni

.gui-switch {
  display: flex;
  align-items: center;
  gap: 2ch;
  justify-content: space-between;
}

Rozszerzanie i modyfikowanie układu flexbox przebiega tak samo jak w przypadku każdego innego układu flexbox. Aby na przykład umieścić etykiety nad lub pod przełącznikiem albo zmienić ich flex-direction:

Narzędzia deweloperskie Flexbox nakładane na pionową etykietę i przełącznik.

<label for="light-switch" class="gui-switch" style="flex-direction: column">
  Default
  <input type="checkbox" role="switch" id="light-switch">
</label>

Monitoruj

Pole wyboru ma styl ścieżki przełącznika – w tym przypadku usunięto normalny element appearance: checkbox i podano zamiast niego własny rozmiar:

Siatka z Narzędziami deweloperskimi nakładającymi się na ścieżkę przełączania, pokazującą nazwane obszary siatki o nazwie „ścieżka”.

.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;
}

Utwór tworzy też siatkę z pojedynczych komórek, w której można umieścić miniaturę do zgłoszenia.

Miniatura

Styl appearance: none usuwa też znacznik wyboru dostarczany przez przeglądarkę. Ten komponent używa pseudoelementupseudoklasy :checked w danych wejściowych, aby zastąpić ten wizualny wskaźnik.

Miniatura to pseudoelement podrzędny dołączony do input[type="checkbox"], który jest umieszczany na wierzchu ścieżki zamiast pod nią, zajmując obszar siatki:track

Narzędzia dla programistów pokazujące miniaturę pseudoelementu umieszczoną w siatkowaniu CSS.

.gui-switch > input::before {
  content: "";
  grid-area: track;
  inline-size: var(--thumb-size);
  block-size: var(--thumb-size);
}

Style

Właściwości niestandardowe umożliwiają wszechstronny komponent przełącznika, który dostosowuje się do schematów kolorów, języków z pisemiem od prawej do lewej i preferencji dotyczących ruchu.

Porównanie jasnego i ciemnego motywu przełącznika oraz jego stanów.

Style interakcji dotykowe

Na urządzeniach mobilnych przeglądarki dodają podświetlenia dotykiem i funkcje zaznaczania tekstu do etykiet i wprowadzanych danych. Te problemy negatywnie wpływały na styl i wizualne interakcje, które były potrzebne do przeprowadzenia tej zmiany. Za pomocą kilku linii kodu CSS mogę usunąć te efekty i dodać własny styl cursor: pointer:

.gui-switch {
  cursor: pointer;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
}

Nie zawsze warto usuwać te style, ponieważ mogą one stanowić cenny wizualny element interakcji. Pamiętaj, aby w przypadku ich usunięcia udostępnić niestandardowe alternatywne wersje.

Monitoruj

Style tego elementu dotyczą głównie jego kształtu i koloru, do których uzyskuje dostęp z elementu nadrzędnego .gui-switch za pomocą kaskady.

Zmień wersje z niestandardowymi rozmiarami i kolorami ścieżek.

.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);
}

Szeroki wybór opcji dostosowywania ścieżki przełącznika pochodzi z 4 właściwości niestandardowych. Dodano border: none, ponieważ appearance: none nie usuwa obramowań z pola wyboru we wszystkich przeglądarkach.

Miniatura

Element miniatury znajduje się już po prawej stronie track, ale wymaga stylu koła:

.gui-switch > input::before {
  background: var(--thumb-color);
  border-radius: 50%;
}

Narzędzia deweloperskie z podświetlonym pseudoelementem miniatury koła.

Interakcja

Użyj właściwości niestandardowych, aby przygotować się na interakcje, które będą wyświetlać podświetlenia podczas najechania kursorem i zmiany pozycji palca. Przed przejściem na styl animacji lub podświetlenia najechania uwzględnia się preferencje użytkownika.

.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;
  }}
}

Pozycja kciuka

Własne właściwości stanowią mechanizm pojedynczego źródła do pozycjonowania miniatury na ścieżce. Mamy do dyspozycji rozmiary ścieżki i miniatury, których użyjemy w obliczeniach, aby miniatura była odpowiednio przesunięta i znajdowała się w obrębie ścieżki: 0% i 100%.

Element input jest właścicielem zmiennej pozycji --thumb-position, a pseudoelement kciuka używa jej jako pozycji translateX:

.gui-switch > input {
  --thumb-position: 0%;
}

.gui-switch > input::before {
  transform: translateX(var(--thumb-position));
}

Teraz możesz swobodnie zmieniać --thumb-position z CSS i pseudoklas podanych w elementach pól wyboru. Ponieważ wcześniej ustawiliśmy warunek transition: transform var(--thumb-transition-duration) ease dla tego elementu, zmiany te mogą być animowane:

/* 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)
  );
}

Myślałem, że ta dezagregowana aranżacja działała dobrze. Element kciuka dotyczy tylko 1 stylu: pozycji translateX. Dane wejściowe umożliwiają zarządzanie różną złożonością i obliczeniami.

Branża

Obsługa została zrealizowana za pomocą klasy modyfikatora -vertical, która dodaje do elementu input rotację za pomocą przekształceń CSS.

Obrócenie elementu 3D nie zmienia jednak całkowitej wysokości komponentu, co może zaburzyć układ bloku. Uwzględnij to, używając zmiennych --track-size i --track-padding. Oblicz minimalną ilość miejsca wymaganą do tego, aby przycisk pionowy działał w układzie zgodnie z oczekiwaniami:

.gui-switch.-vertical {
  min-block-size: calc(var(--track-size) + calc(var(--track-padding) * 2));

  & > input {
    transform: rotate(-90deg);
  }
}

(RTL) od prawej do lewej

Razem z moim znajomym Eladem Schecterem stworzyliśmy prototyp wysuwnego menu bocznego z użyciem przekształceń CSS, które obsługują języki zapisywane od prawej do lewej, przez odwrócenie jednej zmiennej. Zrobiliśmy to, ponieważ w CSS nie ma logicznych przekształceń właściwości i mogą nigdy nie wystąpić. Elad wpadł na świetny pomysł, aby za pomocą niestandardowej wartości właściwości odwrócić wartości procentowe, co umożliwiłoby zarządzanie pojedynczą lokalizacją za pomocą naszej niestandardowej logiki do transformacji logicznych. Użyłem tej samej techniki w tym przypadku i myślę, że bardzo dobrze się sprawdziła:

.gui-switch {
  --isLTR: 1;

  &:dir(rtl) {
    --isLTR: -1;
  }
}

Właściwość niestandardowa o nazwie --isLTR początkowo zawiera wartość 1, co oznacza, że jest to true, ponieważ nasz układ jest domyślnie zorientowany poziomo. Następnie za pomocą pseudoklasy CSS :dir() wartość jest ustawiana na -1, gdy komponent znajduje się w układzie od prawej do lewej.

Użyj funkcji --isLTR, używając jej w ramach funkcji calc() w transformacji:

.gui-switch.-vertical > input {
  transform: rotate(-90deg);
  transform: rotate(calc(90deg * var(--isLTR) * -1));
}

Teraz obrót pionowy przełącznika na pozycję po przeciwnej stronie wymaganą przez układ od prawej do lewej.

Transformacje translateX w pseudoelemencie miniatury też trzeba zaktualizować, aby uwzględnić wymagania dotyczące przeciwnej strony:

.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)
  );
}

Chociaż to podejście nie rozwiąże wszystkich problemów związanych z koncepcją, taką jak transformacje logiczne w CSS, to w wielu przypadkach umożliwia stosowanie zasad DRY.

Stany

Korzystanie z wbudowanego input[type="checkbox"] nie byłoby kompletne bez obsługi różnych stanów, w których może się on znajdować: :checked, :disabled, :indeterminate:hover. :focus został celowo pozostawiony bez zmian, z jedynie skorygowanym przesunięciem. Pierścień ostrości wyglądał świetnie w Firefox i Safari:

Zrzut ekranu przedstawiający pierścień ostrości zaznaczony na przełączniku w Firefoksie i Safari.

Zaznaczono

<label for="switch-checked" class="gui-switch">
  Default
  <input type="checkbox" role="switch" id="switch-checked" checked="true">
</label>

Ten stan reprezentuje stan on. W tym stanie kolor tła „ścieżki” wejścia jest ustawiony na aktywny kolor, a pozycja kursora na „koniec”.

.gui-switch > input:checked {
  background: var(--track-color-active);
  --thumb-position: calc((var(--track-size) - 100%) * var(--isLTR));
}

Wyłączono

<label for="switch-disabled" class="gui-switch">
  Default
  <input type="checkbox" role="switch" id="switch-disabled" disabled="true">
</label>

Przycisk :disabled nie tylko wygląda inaczej, ale też sprawia, że element jest niezmienny. Niezmienność interakcji nie zależy od przeglądarki, ale stany wizualne wymagają stylów ze względu na użycie appearance: none.

.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%);
    }}
  }
}

Przełącznik w ciemnym stylu w wyłączonym, zaznaczonym i niezaznaczonym stanie.

Ten stan jest trudny, ponieważ wymaga motywów ciemnego i jasnego w obu stanach: wyłączonym i zaznaczonym. Stylistycznie wybrałam minimalne style dla tych stanów, aby zmniejszyć obciążenie pracą różnych stylów.

Nieokreślona

Często zapominanym stanem jest :indeterminate, w którym pole wyboru nie jest ani zaznaczone, ani odznaczone. To stan zabawy, jest zachęcający i skromny. Pamiętaj, że stany logiczne mogą mieć stany pośrednie.

Ustawienie pola wyboru jako nieokreślonego jest trudne, ponieważ może to zrobić tylko 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>

Nieokreślony stan z kciukiem w środku ścieżki, co oznacza niezdecydowany.

Stan ten jest dla mnie skromny i zachęcający, więc uznałem, że pozycja przełącznika w środku jest odpowiednia:

.gui-switch > input:indeterminate {
  --thumb-position: calc(
    calc(calc(var(--track-size) / 2) - calc(var(--thumb-size) / 2))
    * var(--isLTR)
  );
}

Najechanie

Interakcje z kursorem powinny zapewniać wizualne wsparcie dla połączonego interfejsu użytkownika, a także wskazywać kierunek interakcji z interfejsem interaktywnym. Ten przełącznik podświetla kciuk półprzezroczystym pierścieniem po najechaniu na etykietę lub dane wejściowe. Animacja najechania wskazuje na interaktywny element miniatury.

Efekt „podświetlenia” jest realizowany za pomocą funkcji box-shadow. Po najechaniu kursorem na niewyłączone pole wejściowe zwiększ rozmiar --highlight-size. Jeśli użytkownik zgadza się na ruch, przechodzimy do box-shadow i obserwujemy jego wzrost. Jeśli nie zgadza się na ruch, wyróżnienie pojawia się natychmiast:

.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

W moim odczuciu przełącznik może być niepokojący, ponieważ próbuje emulować interfejs fizyczny, zwłaszcza ten z okręgiem wewnątrz ścieżki. iOS dobrze sobie z tym poradził, umożliwiając przeciąganie przełącznika na boki. To bardzo przydatna opcja. Z drugiej strony, element interfejsu może wydawać się nieaktywny, jeśli użytkownik spróbuje go przeciągnąć, a nic się nie stanie.

Przeciągane kciuki

Pseudoelement kciuka otrzymuje swoją pozycję z var(--thumb-position) o zakresie .gui-switch > input. JavaScript może dostarczyć wbudowaną wartość stylu w danych wejściowych, aby dynamicznie aktualizować pozycję kciuka, przez co wydaje się, że podąża on za gestem wskaźnika. Po zwolnieniu wskaźnika usuń style wbudowane i za pomocą właściwości niestandardowej --thumb-position określ, czy przeciąganie było bliżej pozycji wyłączonej czy włączonej. Jest to szkielet rozwiązania: zdarzenia wskaźnika warunkowo śledzą pozycje wskaźników w celu modyfikacji właściwości niestandardowych CSS.

Ponieważ komponent działał już w 100% przed wyświetleniem tego skryptu, wymaga to sporo pracy, aby zachować dotychczasowe działanie, np. klikania etykiety w celu przełączania danych wejściowych. Kod JavaScript nie powinien dodawać funkcji kosztem dotychczasowych funkcji.

touch-action

Przeciąganie to gest, który jest niestandardowy, co czyni go idealnym kandydatem na funkcję touch-action. W przypadku tego przełącznika gest poziomy powinien być obsługiwany przez nasz skrypt lub gest pionowy – w przypadku opcji przełącznika pionowego. Za pomocą touch-action możemy powiedzieć przeglądarce, jakie gesty mają być obsługiwane w tym elemencie, aby skrypt mógł obsługiwać gest bez konkurencji.

Ten kod CSS instruuje przeglądarkę, że gdy gest wskaźnika rozpoczyna się w ramach tego przełącznika, należy obsłużyć gesty pionowe, a nie poziome:

.gui-switch > input {
  touch-action: pan-y;
}

Pożądanym efektem jest gest poziomy, który nie powoduje przesuwania ani przewijania strony. Wskaźnik może przewijać pionowo od początku w ramach pola wejściowego i przewijać stronę, ale poziome przewijanie jest obsługiwane w sposób niestandardowy.

Narzędzia związane ze stylem wartości Pixela

Podczas konfiguracji i przeciągania z elementów trzeba pobrać różne obliczone wartości liczbowe. Podane niżej funkcje JavaScriptu zwracają obliczony rozmiar pikseli na podstawie właściwości CSS. Jest używany w skrypcie konfiguracji w ten sposób: 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,
}

Zwróć uwagę, że funkcja window.getComputedStyle() przyjmuje drugi argument, czyli docelowy pseudoelement. Fajnie, że JavaScript może odczytać tak wiele wartości z elementów, nawet z elementów pseudo.

dragging

Jest to kluczowy moment w logice przeciągania. W modułu obsługi zdarzeń funkcji należy zwrócić uwagę na kilka kwestii:

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`)
}

Głównym elementem skryptu jest state.activethumb, czyli mały okrąg, który skrypt umieszcza wraz z wskaźnikiem. Obiekt switches to Map(), gdzie klucze to .gui-switch, a wartości to zapisane w pamięci podręcznej granice i rozmiary, które zapewniają wydajność skryptu. Obsługa tekstu od prawej do lewej jest realizowana za pomocą tej samej właściwości niestandardowej, która jest używana w CSS (--isLTR), i może służyć do odwrócenia logiki i dalszego obsługiwania tekstu w układzie poziomym. Wartość event.offsetX jest również przydatna, ponieważ zawiera wartość delta, która jest przydatna do pozycjonowania palca.

state.activethumb.style.setProperty('--thumb-position', `${track + pos}px`)

Ten ostatni wiersz kodu CSS ustawia właściwość niestandardową używaną przez element miniatury. W przeciwnym razie to przypisanie wartości mogłoby zostać przeniesione z czasem, ale poprzednie zdarzenie wskaźnika tymczasowo ustawiło --thumb-transition-duration na 0s, usuwając to, co byłoby powolną interakcją.

dragEnd

Aby umożliwić użytkownikowi przeciąganie poza przełącznik i puszczenie, należy zarejestrować globalne zdarzenie okna:

window.addEventListener('pointerup', event => {
  if (!state.activethumb) return

  dragEnd(event)
})

Uważam, że bardzo ważne jest, aby użytkownik mógł swobodnie przeciągać i interfejs był na tyle inteligentny, by to uwzględnić. Nie wymagało to wiele pracy, ale wymagało dokładnego przeanalizowania podczas procesu tworzenia.

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()
}

Interakcja z elementem została zakończona, więc czas ustawić właściwość inputChecked i usunąć wszystkie zdarzenia gestów. Pole wyboru zmieni się na state.activethumb.checked = determineChecked().

determineChecked()

Ta funkcja, wywoływana przez dragEnd, określa, gdzie znajduje się aktualny palec wskazujący w obrębie granic ścieżki, i zwraca wartość true, jeśli palec znajduje się na połowie ścieżki lub dalej:

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
}

Dodatkowe uwagi

Gest przeciągania spowodował powstanie pewnego zadłużenia kodu ze względu na wybraną początkową strukturę HTML, głównie z powodu owijania danych wejściowych w etykiecie. Etykieta będąca elementem nadrzędnym otrzymuje po danych wejściowych interakcje związane z kliknięciami. Na końcu zdarzenia dragEnd udało Ci się zauważyć funkcję padRelease() jako funkcję, która brzmi dziwnie.

const padRelease = () => {
  state.recentlyDragged = true

  setTimeout(_ => {
    state.recentlyDragged = false
  }, 300)
}

Ma to na celu uwzględnienie przyczyny późniejszego kliknięcia etykiety, ponieważ spowoduje to odznaczenie lub sprawdzenie interakcji użytkownika.

Gdybym miał to robić jeszcze raz, można rozważyć dostosowanie DOM za pomocą JavaScriptu podczas uaktualniania interfejsu użytkownika, aby utworzyć element, który sam obsługuje kliknięcia etykiety i nie koliduje z wbudowanym działaniem.

Ten rodzaj kodu JavaScript jest najmniej lubianym przeze mnie, ponieważ nie chcę zarządzać warunkowym przenoszeniem zdarzeń:

const preventBubbles = event => {
  if (state.recentlyDragged)
    event.preventDefault() && event.stopPropagation()
}

Podsumowanie

Ten mały komponent przełącznika okazał się najbardziej czasochłonnym ze wszystkich dotychczasowych wyzwań związanych z GUI. Teraz, gdy już wiesz, jak to zrobić, jak Ty to zrobisz? 🙂

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 utworzone przez społeczność

Zasoby

Znajdź .gui-switch kod źródłowy na GitHubie.