switch 구성요소 빌드

반응성이 뛰어나고 액세스 가능한 switch 구성요소를 빌드하는 방법에 관한 기본적인 개요입니다.

이 게시물에서는 스위치 구성요소를 빌드하는 방법에 관한 생각을 공유하고자 합니다. 데모 사용해 보기

데모

동영상을 선호한다면 이 게시물의 YouTube 버전을 참조하세요.

개요

스위치는 체크박스와 비슷하게 작동하지만 불리언 켜기/끄기 상태를 명시적으로 나타냅니다.

이 데모는 대부분의 기능에 <input type="checkbox" role="switch">를 사용하므로 CSS나 JavaScript가 없어도 제대로 작동하고 액세스할 수 있다는 이점이 있습니다. CSS를 로드하면 오른쪽에서 왼쪽으로 쓰는 언어, 세로성, 애니메이션 등을 지원합니다. JavaScript를 로드하면 스위치를 드래그하고 유형화할 수 있습니다.

맞춤 속성

다음 변수는 스위치의 다양한 부분과 옵션을 나타냅니다. 최상위 클래스인 .gui-switch에는 구성요소 하위 요소 전반에 사용되는 맞춤 속성과 중앙 집중식 맞춤설정을 위한 진입점이 포함됩니다.

추적

길이 (--track-size), 패딩, 2가지 색상:

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

칼림바

크기, 배경 색상 및 상호작용 강조표시 색상은 다음과 같습니다.

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

움직임 감소

명확한 별칭을 추가하고 반복을 줄이려면 미디어 쿼리 5의 초안 사양에 따라 PostCSS 플러그인을 사용하여 축소된 모션 환경설정 사용자 미디어 쿼리를 맞춤 속성에 입력할 수 있습니다.

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

마크업

저는 <input type="checkbox" role="switch"> 요소를 <label>로 래핑하여 체크박스와 라벨 연결이 모호하지 않도록 관계를 번들로 묶는 동시에 사용자가 라벨과 상호작용하여 입력을 전환할 수 있도록 했습니다.

스타일이 지정되지 않은 자연스러운 라벨 및 체크박스

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

<input type="checkbox">APIstate와 함께 사전 빌드됩니다. 브라우저는 checked 속성과 oninputonchanged와 같은 입력 이벤트를 관리합니다.

레이아웃

Flexbox, 그리드, 맞춤 속성은 이 구성요소의 스타일을 유지하는 데 중요합니다. 이러한 API는 값을 중앙 집중화하고, 모호한 계산이나 영역에 이름을 지정하며, 소규모 맞춤 속성 API를 사용하여 구성요소를 쉽게 맞춤설정할 수 있도록 합니다.

.gui-switch

스위치의 최상위 레이아웃은 flexbox입니다. .gui-switch 클래스에는 하위 요소가 레이아웃을 계산하는 데 사용하는 비공개 및 공개 맞춤 속성이 포함되어 있습니다.

가로 라벨과 스위치를 오버레이하여 공간의 레이아웃 분포를 보여주는 Flexbox DevTools

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

Flexbox 레이아웃을 확장하고 수정하는 것은 Flexbox 레이아웃을 변경하는 것과 같습니다. 예를 들어 스위치 위 또는 아래에 라벨을 넣거나 flex-direction를 변경하는 방법은 다음과 같습니다.

세로 라벨과 스위치를 오버레이하는 Flexbox DevTools

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

추적

체크박스 입력은 일반 appearance: checkbox를 삭제하고 자체 크기를 대신 제공하여 스위치 트랙으로 스타일이 지정됩니다.

그리드 DevTools가 스위치 트랙을 오버레이하고 이름이 &#39;track&#39;인 이름이 지정된 그리드 트랙 영역을 표시합니다.

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

또한 트랙은 엄지손가락으로 소유권을 주장할 1x1 단일 셀 그리드 트랙 영역도 만듭니다.

칼림바

appearance: none 스타일은 브라우저에서 제공하는 시각적 체크표시도 삭제합니다. 이 구성요소는 입력에 의사 요소:checked 유사 클래스를 사용하여 이 시각적 표시기를 대체합니다.

thumb은 input[type="checkbox"]에 연결된 유사 요소 하위 요소이며 그리드 영역 track을 신청하여 트랙 아래가 아닌 트랙 위에 스택합니다.

CSS 그리드 내에 있는 pseudo-element thumb을 보여주는 DevTools

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

스타일

맞춤 속성을 사용하면 색상 체계, 오른쪽에서 왼쪽으로 쓰는 언어, 모션 환경설정에 맞게 조정되는 다목적 스위치 구성요소가 가능합니다.

스위치와 그 상태의 밝은 테마와 어두운 테마를 나란히 비교

터치 상호작용 스타일

모바일에서 브라우저는 라벨 및 입력에 탭 강조표시 및 텍스트 선택 기능을 추가합니다. 이는 이 전환에 필요한 스타일과 시각적 상호작용 피드백에 부정적인 영향을 미쳤습니다. 몇 줄의 CSS로 이러한 효과를 삭제하고 나만의 cursor: pointer 스타일을 추가할 수 있습니다.

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

이러한 스타일은 시각적 상호작용 피드백이 될 수 있으므로 항상 이러한 스타일을 삭제하는 것이 바람직한 것은 아닙니다. 맞춤 대안을 삭제하는 경우 맞춤 대안을 제공해야 합니다.

추적

이 요소의 스타일은 대부분 모양과 색상에 관한 것으로, 캐스케이드를 통해 상위 .gui-switch에서 액세스합니다.

맞춤 트랙 크기 및 색상이 있는 스위치 변형

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

스위치 트랙의 다양한 맞춤설정 옵션은 4가지 맞춤 속성에서 제공됩니다. appearance: none가 모든 브라우저의 체크박스에서 테두리를 삭제하지 않으므로 border: none가 추가됩니다.

칼림바

thumb 요소가 이미 오른쪽 track에 있지만 원 스타일이 필요합니다.

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

원 thumb pseudo-element가 강조 표시된 DevTools

상호작용

맞춤 속성을 사용하여 마우스 오버 하이라이트 및 엄지손가락 위치 변경사항을 표시하는 상호작용을 준비합니다. 모션 또는 마우스 오버 하이라이트 스타일을 전환하기 전에 사용자의 환경설정도 확인합니다.

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

엄지손가락 위치

맞춤 속성은 트랙에 thumb을 배치하기 위한 단일 소스 메커니즘을 제공합니다. thumb을 제대로 오프셋을 유지하고 트랙 내에서 0%100% 사이를 유지하도록 계산에 사용할 트랙과 thumb 크기를 원하는 대로 사용할 수 있습니다.

input 요소는 위치 변수 --thumb-position를 소유하고 thumb 유사 요소는 이를 translateX 위치로 사용합니다.

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

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

이제 체크박스 요소에 제공된 CSS 및 의사 클래스에서 --thumb-position를 자유롭게 변경할 수 있습니다. 이 요소 앞부분에 조건부로 transition: transform var(--thumb-transition-duration) ease를 설정했으므로 이러한 변경사항은 변경될 때 애니메이션으로 표시될 수 있습니다.

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

이 분리된 오케스트레이션은 잘 작동한다고 생각했습니다. thumb 요소는 한 가지 스타일, 즉 translateX 위치와만 관련이 있습니다. 입력으로 모든 복잡성과 계산을 관리할 수 있습니다.

카테고리

CSS 변환이 포함된 회전을 input 요소에 추가하는 수정자 클래스 -vertical를 사용하여 지원했습니다.

하지만 3D 회전된 요소는 구성요소의 전체 높이를 변경하지 않으므로 블록 레이아웃이 삭제될 수 있습니다. --track-size--track-padding 변수를 사용하여 이를 고려합니다. 예상한 대로 세로 버튼이 레이아웃에서 흐르는 데 필요한 최소 공간을 계산합니다.

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

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

(RTL) 오른쪽에서 왼쪽

CSS 친구 Elad Schecter와 제가 단일 변수를 뒤집어 오른쪽에서 왼쪽으로 언어를 처리하는 CSS 변환을 사용하여 슬라이드 아웃 사이드 메뉴의 프로토타입을 함께 제작했습니다. CSS에 논리적 속성 변환이 없으며 전혀 변환이 없을 수도 있기 때문에 이렇게 했습니다. 엘라드는 논리 변환을 위한 자체 맞춤 로직의 단일 위치 관리를 허용하기 위해 맞춤 속성 값을 사용하여 백분율을 반전시킬 수 있는 좋은 아이디어를 가지고 있었습니다. 이 전환에서도 같은 기법을 사용했고 효과가 좋았습니다.

.gui-switch {
  --isLTR: 1;

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

--isLTR라는 맞춤 속성은 처음에 1 값을 보유합니다. 즉, 레이아웃은 기본적으로 왼쪽에서 오른쪽이므로 true입니다. 그런 다음 CSS 의사 클래스 :dir()를 사용하여 구성요소가 오른쪽에서 왼쪽 레이아웃 내에 있을 때 값이 -1로 설정됩니다.

변환 내부의 calc() 내에서 사용하여 --isLTR를 실행합니다.

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

이제 세로 스위치의 회전은 오른쪽에서 왼쪽 레이아웃에 필요한 반대쪽 위치를 고려합니다.

thumb 유사 요소의 translateX 변환도 반대 측 요구사항을 고려하여 업데이트해야 합니다.

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

이 접근 방식이 논리적 CSS 변환과 같은 개념과 관련된 모든 요구사항을 해결하는 데 도움이 되지는 않지만, 많은 사용 사례에 몇 가지 DRY 원칙을 제공합니다.

상태

기본 제공 input[type="checkbox"]를 사용하려면 다양한 상태(:checked, :disabled, :indeterminate, :hover)를 처리해야 합니다. :focus는 의도적으로 홀로 남겨졌고 오프셋만 조정되었습니다. 포커스 링은 Firefox와 Safari에서 잘 보였습니다.

Firefox 및 Safari의 스위치에 포커스가 맞춰진 포커스 링의 스크린샷.

선택됨

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

이 상태는 on 상태를 나타냅니다. 이 상태에서 입력 '트랙' 배경은 활성 색상으로 설정되고 엄지손가락 위치는 '끝'으로 설정됩니다.

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

사용 중지됨

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

:disabled 버튼은 시각적으로 달라 보일 뿐만 아니라 요소를 변경할 수 없게 만들어야 합니다.상호작용 불변성은 브라우저에서 일어날 수 없지만 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%);
    }}
  }
}

사용 중지, 선택, 선택 해제된 상태의 어두운 스타일 스위치

이 상태는 사용 중지 상태와 선택 상태가 모두 있는 어두운 테마와 밝은 테마가 필요하므로 까다롭습니다. 스타일 조합의 유지보수 부담을 덜기 위해 스타일에 따라 이러한 상태의 최소 스타일을 선택했습니다.

불확실

자주 잊어버리는 상태는 :indeterminate이며, 여기서는 체크박스가 선택되거나 선택 해제되지 않습니다. 재미있는 상태이며 매력적이고 소박합니다. 불리언 상태는 상태 간에 부적절한 경우가 있을 수 있습니다.

체크박스를 미확정으로 설정하는 것은 까다롭기 때문에 자바스크립트만 설정할 수 있습니다.

<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>

중앙에 트랙 thumb이 있는 미확정 상태로, 결정되지 않았음을 나타냅니다.

제 생각에는 상태가 소박하고 매력적이기 때문에 스위치 썸 위치를 중앙에 배치하는 것이 적절했습니다.

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

마우스 오버

마우스 오버 상호작용은 연결된 UI를 시각적으로 지원하고 대화형 UI를 가리키는 방향도 제공해야 합니다. 이 스위치는 라벨이나 입력에 마우스를 가져가면 thumb에 반투명 링으로 강조 표시됩니다. 그러면 이 마우스 오버 애니메이션은 상호작용 thumb 요소를 향한 방향을 제공합니다.

'강조 표시' 효과는 box-shadow로 실행됩니다. 사용 중지되지 않은 입력의 마우스 오버 시 --highlight-size의 크기를 늘립니다. 사용자가 모션에 동의한다면 box-shadow를 전환하고 확대하는 것을 확인합니다. 사용자가 모션이 마음에 들지 않으면 강조표시가 즉시 표시됩니다.

.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

스위치 인터페이스는 특히 트랙 안에 원이 있는 이런 종류의 인터페이스를 흉내 낼 때 이상하게 느껴질 수 있습니다. iOS는 스위치를 좌우로 드래그할 수 있고 이 옵션이 있다는 점이 매우 만족스럽습니다. 반대로 드래그 동작을 시도했지만 아무 일도 일어나지 않으면 UI 요소가 비활성으로 느껴질 수 있습니다.

드래그 가능한 thumb

thumb 유사 요소는 .gui-switch > input 범위가 지정된 var(--thumb-position)에서 위치를 수신합니다. JavaScript는 입력에 인라인 스타일 값을 제공하여 thumb 위치를 포인터 동작을 따르는 것처럼 보이게 만들 수 있습니다. 포인터가 해제되면 인라인 스타일을 삭제하고 맞춤 속성 --thumb-position를 사용하여 드래그가 꺼졌거나 켜져 있는지 확인합니다. 이는 솔루션의 핵심입니다. 포인터 이벤트는 조건부로 포인터 위치를 추적하여 CSS 맞춤 속성을 수정합니다.

이 스크립트가 표시되기 전에는 구성요소가 이미 100% 작동했으므로 기존 동작을 유지하려면 라벨을 클릭하여 입력을 전환하는 등 상당한 작업이 필요합니다. JavaScript에서 기존 기능을 유지하면서 기능을 추가해서는 안 됩니다.

touch-action

드래그는 맞춤 동작이므로 touch-action의 이점을 활용하기에 좋습니다. 이 스위치의 경우 가로 동작은 Google 스크립트로 처리되거나 세로 전환 변형을 위해 캡처된 세로 동작이 처리되어야 합니다. touch-action를 사용하면 이 요소에서 처리할 동작을 브라우저에 알리므로 스크립트가 경쟁 없이 동작을 처리할 수 있습니다.

다음 CSS는 전환 트랙 내에서 포인터 동작이 시작되면 세로 동작을 처리하고 가로 동작으로는 아무것도 하지 않도록 브라우저에 지시합니다.

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

원하는 결과는 페이지를 이동하거나 스크롤하지 않는 가로 동작입니다. 포인터는 입력 내에서 수직으로 스크롤하여 페이지를 스크롤할 수 있지만 가로 방향은 맞춤 처리됩니다.

픽셀 값 스타일 유틸리티

설정 시 및 드래그 중에 계산된 다양한 숫자 값을 요소에서 가져와야 합니다. 다음 자바스크립트 함수는 CSS 속성이 주어진 경우 계산된 픽셀 값을 반환합니다. 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,
}

window.getComputedStyle()가 두 번째 인수인 타겟 의사 요소를 허용하는 방식을 확인하세요. JavaScript가 유사 요소에서도 매우 많은 값을 요소에서 읽을 수 있다는 점은 꽤 멋집니다.

dragging

드래그 로직의 핵심 시점이며 함수 이벤트 핸들러에서 알아야 할 몇 가지 사항이 있습니다.

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

스크립트 히어로는 이 스크립트가 포인터와 함께 배치하는 작은 원인 state.activethumb입니다. switches 객체는 Map()이며, 여기서 키는 .gui-switch이고 값은 스크립트를 효율적으로 유지하는 캐시된 경계 및 크기입니다. 오른쪽에서 왼쪽으로는 CSS가 --isLTR인 동일한 맞춤 속성을 사용하여 처리되며 이 속성을 사용하여 로직을 반전하고 RTL을 계속 지원할 수 있습니다. event.offsetX도 중요합니다. thumb을 배치하는 데 유용한 델타 값을 포함하고 있기 때문입니다.

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

CSS의 마지막 줄은 thumb 요소에서 사용하는 맞춤 속성을 설정합니다. 그렇지 않으면 이 값 할당은 시간이 지남에 따라 전환되지만 이전 포인터 이벤트에서 일시적으로 --thumb-transition-duration0s로 설정하여 느린 상호작용이 삭제되었을 것입니다.

dragEnd

사용자가 스위치 바깥으로 드래그하고 손을 뗄 수 있도록 하려면 전역 창 이벤트를 등록해야 합니다.

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

  dragEnd(event)
})

사용자가 느슨하게 드래그할 수 있고 인터페이스를 감당할 수 있을 만큼 똑똑해야 합니다. 이 스위치로 처리하는 데는 별다른 작업이 필요하지 않았지만 개발 프로세스 중에 신중하게 고려해야 했습니다.

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

요소와의 상호작용이 완료되었습니다. 입력 확인 속성을 설정하고 모든 동작 이벤트를 삭제할 시간입니다. 체크박스는 state.activethumb.checked = determineChecked()로 변경됩니다.

determineChecked()

dragEnd에 의해 호출되는 이 함수는 thumb 전류가 트랙의 경계 내에서 어디에 있는지 확인하고 트랙의 절반 이상이면 true를 반환합니다.

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
}

추가적인 생각

드래그 동작은 초기 HTML 구조 선택으로 인해 약간의 코드 부채가 발생했으며, 특히 입력을 라벨로 래핑했습니다. 상위 요소인 라벨은 입력 후 클릭 상호작용을 수신합니다. dragEnd 이벤트가 끝날 때 padRelease()가 이상하게 들린 함수로 인식되었을 수 있습니다.

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

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

이는 사용자가 수행한 상호작용을 선택 해제하거나 확인하므로 나중에 클릭하는 라벨을 설명하기 위한 것입니다.

이 작업을 다시 하려면 라벨 클릭 자체를 처리하고 기본 제공 동작과 싸우지 않는 요소를 만들기 위해 UX 업그레이드 중에 JavaScript로 DOM을 조정하는 것이 좋습니다.

이런 종류의 JavaScript는 제가 가장 작성하고 싶지 않은 글로, 조건부 이벤트 버블링을 관리하지 않겠습니다.

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

결론

이 작은 switch 구성요소는 지금까지 모든 GUI 챌린지 중 가장 많은 작업이 되었습니다. 이제 제가 어떻게 했는지 알았으니 어떻게 되세요?‽ 🙂

접근 방식을 다양화하고 웹에서 빌드하는 모든 방법을 알아보겠습니다. 데모를 만들고 링크를 트윗해 주세요. 그러면 아래의 커뮤니티 리믹스 섹션에 추가하겠습니다.

커뮤니티 리믹스

자료

.gui-switch GitHub 소스 코드를 찾습니다.