Tworzenie komponentu przełącznika

Podstawowe informacje o tym, jak tworzyć elastyczne i dostępne komponenty przełączników.

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

Demonstracja

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 kodu CSS ani JavaScriptu, aby działać w pełni i być dostępnym. Wczytywanie CSS umożliwia obsługę języków zapisywanych od prawej do lewej, animacji, pionowości 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), wypeł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%);
  }
}

Mniej animacji

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 Media Queries5:

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

Znacznik

Element <input type="checkbox" role="switch"> został zapakowany w element <label>, aby utworzyć ich relację i uniknąć niejasności w powiązaniu pola wyboru i etykiety, 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"> jest wstępnie skompilowany z interfejsem APIstanem. 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:

Nakładka Flexbox w Narzędziach deweloperskich z etykietą pionową i przełącznikiem.

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

Monitoruj

Pole wyboru jest stylizowane jako przełącznik, ponieważ ma normalny rozmiarappearance: checkbox i własny rozmiar:

Nakładka narzędzia DevTools na ścieżkę przełącznika, pokazująca nazwane obszary ścieżki siatki o nazwie „track”.

.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 jest pseudoelementem podrzędnym dołączonym do input[type="checkbox"] i znajduje się 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 pisaniem od prawej do lewej i preferencji dotyczących animacji.

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

Style interakcji dotykowych

Na urządzeniach mobilnych przeglądarki dodają do etykiet i elementów wejściowych funkcje podświetlenia i wybierania tekstu. 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. Jeśli je usuniesz, pamiętaj, aby podać alternatywne wersje niestandardowe.

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.

Wersje przełącznika z niestandardowymi rozmiarami i kolorami ścieżki.

.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łączania pochodzi z 4 właściwości niestandardowych. border: none jest dodawany, ponieważ appearance: none nie usuwa obramowania 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 miniatury. 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. Do dyspozycji mamy 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 ma zmienną pozycji --thumb-position, a pseudoelement miniatury 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ć wartość --thumb-position w CSS i pseudoklasach podanych w elementach checkbox. 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 dobrze. Element miniatury dotyczy tylko jednego stylu, pozycji translateX. Dane wejściowe mogą zarządzać całą 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 ogólnej wysokości komponentu, co może zaburzyć układ bloku. Uwzględnij to za pomocą zmiennych --track-size--track-padding. Oblicz minimalną ilość miejsca wymaganą dla przycisku pionowego, aby pasował do układu:

.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 żadnych przekształceń właściwości logicznych i może się to nigdy nie zmienić. Elad wpadł na świetny pomysł, aby użyć niestandardowej wartości właściwości w celu odwrócenia wartości procentowych i umożliwienia zarządzania pojedynczym miejscem docelowym 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 z pierścieniem fokusu skupionym na przełączniku w Firefox 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 wymaga interakcji z przeglądarką, 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 motywu ciemnego i jasnego w obu stanach: wyłączonym i zaznaczonym. Wybrałem minimalne style dla tych stanów, aby ułatwić utrzymanie kombinacji stylów.

Nieokreślony

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>

Stan nieokreślony, w którym miniatura utworu znajduje się pośrodku, co oznacza, że nie podjęto jeszcze decyzji.

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 wyróżnia kciuk półprzezroczystym pierścieniem, gdy najedziesz na etykietę lub pole wprowadzania danych. Animacja najechania kursorem wskazuje 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 użytkownik 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 wygodne rozwiązanie. Z drugiej strony, element interfejsu może wydawać się nieaktywny, jeśli użytkownik spróbuje go przeciągnąć, a nic się nie stanie.

Kciuki, które można przeciągać

Pseudoelement miniatury otrzymuje swoją pozycję z .gui-switch > input var(--thumb-position), JavaScript może podać wartość stylu w ramach wejścia, aby dynamicznie aktualizować pozycję miniatury, co sprawia, że wydaje się ona podążać za ruchem 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 podstawa rozwiązania. Zdarzenia wskaźnika warunkowo śledzą pozycje wskaźnika, aby modyfikować właściwości niestandardowe w arkuszu 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, a gesty pionowe powinny być rejestrowane w przypadku wariantu 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ę na tym przełączniku, 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 przesuwać się pionowo w obszarze wprowadzania i po stronie, ale poziome przesunięcia są obsługiwane w sposób niestandardowy.

Narzędzia do stylizacji wartości pikseli

Podczas konfigurowania i przeciągania trzeba będzie pobrać różne wartości liczbowe z elementów. Podane niżej funkcje JavaScriptu zwracają obliczony rozmiar pikseli na podstawie właściwości CSS. Jest on używany w skrypcie konfiguracyjnym w taki 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. To przypisanie wartości normalnie zmieniałoby się z upływem czasu, ale poprzednie zdarzenie wskaźnika tymczasowo ustawiło wartość --thumb-transition-duration na 0s, co wyeliminowałoby opóźnienie interakcji.

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 miał swobodę przeciągania, a interfejs był na tyle inteligentny, aby to uwzględnić. Ta zmiana nie wymagała wiele pracy, ale wymagała 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 zostało zmienione 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, będzie otrzymywać interakcje związane z kliknięciem po wprowadzeniu danych. Na końcu zdarzenia dragEnd możesz zauważyć padRelease() jako dziwnie brzmiącą funkcję.

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

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

Ma to na celu uwzględnienie etykiety, która otrzyma to późniejsze kliknięcie, ponieważ odznaczy lub zaznaczy interakcję wykonaną przez użytkownika.

Gdybym miał to robić jeszcze raz, można rozważyć dostosowanie DOM za pomocą JavaScriptu podczas uaktualniania UX, 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 społeczności

Zasoby

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