Anahtar bileşeni oluşturma

Duyarlı ve erişilebilir bir anahtar bileşeni oluşturma hakkında temel bilgiler.

Bu yayında, anahtar bileşenleri oluşturma yöntemiyle ilgili düşüncelerimi paylaşmak istiyorum. Demoyu deneyin.

Demo

Video tercih ediyorsanız bu yayının YouTube versiyonunu aşağıda bulabilirsiniz:

Genel Bakış

Anahtar, onay kutusuna benzer şekilde çalışır ancak açık ve kapalı durumları açıkça gösterir.

Bu demoda işlevlerin çoğu için <input type="checkbox" role="switch"> kullanılır. Bu, tam işlevsel ve erişilebilir olması için CSS veya JavaScript'in gerekmemesi avantajını sağlar. CSS yükleme, sağdan sola diller, dikey yön, animasyon ve daha fazlası için destek sunar. JavaScript'in yüklenmesi, anahtarı sürüklenebilir ve somut hale getirir.

Özel özellikler

Aşağıdaki değişkenler, anahtarın çeşitli bölümlerini ve seçeneklerini gösterir. Üst düzey sınıf olarak .gui-switch, bileşen alt öğelerinde kullanılan özel özellikleri ve merkezi özelleştirme için giriş noktalarını içerir.

Parça

Uzunluk (--track-size), dolgu ve iki renk:

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

Küçük resim

Boyut, arka plan rengi ve etkileşim vurgu renkleri:

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

İndirgenmiş hareket

Net bir takma ad eklemek ve tekrarı azaltmak için, Media Queries 5'teki bu taslak spesifikasyonuna dayalı olarak PostCSS eklentisi ile özel bir özelliğe azaltılmış hareket tercihi kullanıcı medya sorgusu yerleştirilebilir:

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

Brüt kar

<input type="checkbox" role="switch"> öğemi <label> ile sarmayı tercih ettim. Böylece, onay kutusu ve etiket ilişkilendirme belirsizliğini önlemek için bu öğelerin ilişkisini bir araya getirdim. Ayrıca, kullanıcının girişi açıp kapatmak için etiketle etkileşime geçmesine olanak tanıdım.

Doğal, stil uygulanmamış bir etiket ve onay kutusu.

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

<input type="checkbox">, API ve durum ile birlikte önceden oluşturulmuş olarak gelir. Tarayıcı, checked özelliğini ve oninput ile onchanged gibi giriş etkinliklerini yönetir.

Düzenler

Flexbox, grid ve custom properties, bu bileşenin stillerini korumak için çok önemlidir. Değerleri merkezileştirir, aksi takdirde belirsiz olacak hesaplamalara veya alanlara ad verir ve kolay bileşen özelleştirmeleri için küçük bir özel mülk API'si sağlar.

.gui-switch

Anahtarın üst düzey düzeni flexbox'tır. Sınıf .gui-switch, çocukların düzenlerini hesaplamak için kullandığı özel ve genel özel özellikleri içerir.

Yatay bir etiket ve anahtarın üzerinde yer alan Flexbox DevTools, bunların düzeninin alan dağılımını gösteriyor.

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

Flexbox düzenini genişletmek ve değiştirmek, herhangi bir flexbox düzenini değiştirmek gibidir. Örneğin, etiketleri bir anahtarın üstüne veya altına yerleştirmek ya da flex-direction simgesini değiştirmek için:

Dikey bir etiketin ve anahtarın üzerinde yer alan Flexbox DevTools.

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

Parça

Onay kutusu girişi, normal appearance: checkbox değeri kaldırılarak ve bunun yerine kendi boyutu sağlanarak anahtar yolu olarak şekillendirilir:

Anahtarlama parçasının üzerinde yer alan Kılavuz DevTools yerleşimi, &quot;parça&quot; adlı kılavuz parçası alanlarını gösteriyor.

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

Parça, bir küçük resmin talep edilebileceği bire bir tek hücreli bir ızgara parça alanı da oluşturur.

Küçük resim

appearance: none stili, tarayıcı tarafından sağlanan görsel onay işaretini de kaldırır. Bu bileşen, bu görsel göstergeyi değiştirmek için girişte bir sözde öğe ve :checked sözde sınıfını kullanır.

Başparmak, input[type="checkbox"] öğesine eklenmiş bir sözde öğe alt öğesidir ve ızgara alanını track talep ederek parçanın altında değil, üstünde yer alır:

DevTools, sözde öğe küçük resmini CSS ızgarasının içine yerleştirilmiş olarak gösteriyor.

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

Stiller

Özel özellikler, renk şemalarına, sağdan sola dillerine ve hareket tercihlerine uyum sağlayan çok yönlü bir anahtar bileşeni sağlar.

Anahtarın açık ve koyu teması ile durumlarının yan yana karşılaştırması.

Dokunma etkileşimi stilleri

Mobil cihazlarda tarayıcılar, etiketlere ve girişlere dokunma vurguları ve metin seçimi özellikleri ekler. Bu durum, geçişin gerektirdiği stil ve görsel etkileşim geri bildirimini olumsuz etkiledi. Birkaç satırlık CSS koduyla bu efektleri kaldırıp kendi cursor: pointer stilimi ekleyebilirim:

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

Bu stiller değerli görsel etkileşim geri bildirimleri olabileceğinden, her zaman kaldırılması önerilmez. Kaldırdığınızda özel alternatifler sunduğunuzdan emin olun.

Parça

Bu öğenin stilleri çoğunlukla şekli ve rengiyle ilgilidir. Bu öğe, stillerine basamaklı stil sayfaları aracılığıyla üst öğe .gui-switch üzerinden erişir.

Özel parça boyutları ve renkleri olan anahtar varyantları.

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

Geçiş parçası için çok çeşitli özelleştirme seçenekleri dört özel özellikten gelir. border: none, appearance: none tüm tarayıcılarda onay kutusundaki kenarlıkları kaldırmadığından eklenir.

Küçük resim

Başparmak öğesi zaten sağda track ancak daire stillerine ihtiyacı var:

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

Geliştirici Araçları, daire başparmak sözde öğesini vurgulanmış olarak gösteriliyor.

Etkileşim

Fareyle üzerine gelme vurgularını ve küçük resim konumundaki değişiklikleri gösterecek etkileşimlere hazırlanmak için özel özellikleri kullanın. Hareket veya fareyle üzerine gelme vurgulama stillerine geçiş yapılmadan önce kullanıcının tercihi de kontrol edilir.

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

Başparmak konumu

Özel özellikler, parçadaki küçük resmi konumlandırmak için tek bir kaynak mekanizması sağlar. Kaydırma çubuğunun ve başparmağın boyutları, başparmağın doğru şekilde dengelenmesini ve kaydırma çubuğu içinde kalmasını sağlamak için hesaplamalarda kullanılabilir: 0% ve 100%.

input öğesi, --thumb-position konum değişkenine sahiptir ve thumb sözde öğesi bunu translateX konumu olarak kullanır:

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

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

Artık --thumb-position öğesini CSS'den ve onay kutusu öğelerinde sağlanan sözde sınıflardan değiştirebiliriz. Bu öğede daha önce koşullu olarak transition: transform var(--thumb-transition-duration) ease ayarladığımız için bu değişiklikler değiştirildiğinde animasyonlu olabilir:

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

Bu ayrılmış düzenlemenin iyi çalıştığını düşünüyorum. Başparmak öğesi yalnızca bir stil olan translateX konumuyla ilgilenir. Giriş, tüm karmaşıklığı ve hesaplamaları yönetebilir.

Dikey

Destek, -vertical öğesine CSS dönüşümleriyle döndürme ekleyen bir değiştirici sınıf -vertical ile yapıldı.input

3D döndürülmüş bir öğe, bileşenin genel yüksekliğini değiştirmez ancak bu durum blok düzenini bozabilir. --track-size ve --track-padding değişkenlerini kullanarak bunu hesaba katın. Dikey bir düğmenin düzende beklendiği gibi akması için gereken minimum alanı hesaplayın:

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

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

(RTL) sağdan sola

Bir CSS arkadaşım Elad Schecter ile birlikte, tek bir değişkeni çevirerek sağdan sola dillerin işlendiği CSS dönüşümlerini kullanan kayarak açılan bir yan menü prototipi oluşturduk. Bunun nedeni, CSS'de mantıksal özellik dönüşümlerinin olmaması ve hiçbir zaman olmamasıdır. Elad, mantıksal dönüşümler için kendi özel mantığımızın tek bir konumdan yönetilmesine olanak tanımak amacıyla yüzdeleri tersine çevirmek için özel bir mülk değeri kullanma fikrini ortaya attı. Bu anahtarda da aynı tekniği kullandım ve çok iyi sonuç verdiğini düşünüyorum:

.gui-switch {
  --isLTR: 1;

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

--isLTR adlı özel bir özellik başlangıçta 1 değerini içerir. Bu, düzenimiz varsayılan olarak soldan sağa olduğundan true anlamına gelir. Ardından, CSS sözde sınıfı :dir() kullanılarak bileşen soldan sağa düzen içinde olduğunda değer -1 olarak ayarlanır.

Dönüşüm içinde calc() kullanarak --isLTR öğesini işleme alın:

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

Artık dikey anahtarın dönüşü, sağdan sola düzenin gerektirdiği karşı taraf konumunu hesaba katıyor.

Ayrıca, translateX dönüşümlerinin, karşı taraf şartını hesaba katacak şekilde güncellenmesi gerekir:

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

Bu yaklaşım, mantıksal CSS dönüşümleri gibi bir kavramla ilgili tüm ihtiyaçları karşılamasa da birçok kullanım alanı için bazı DRY (Kendini Tekrar Etme) ilkeleri sunar.

Eyaletler

Yerleşik input[type="checkbox"] özelliğini kullanırken, bu özelliğin bulunabileceği çeşitli durumları (:checked, :disabled, :indeterminate ve :hover) ele almadan olmaz. :focus, yalnızca ofsetinde ayarlama yapılarak kasıtlı olarak yalnız bırakıldı. Odak halkası Firefox ve Safari'de harika görünüyordu:

Firefox ve Safari&#39;de bir anahtara odaklanan odak halkasının ekran görüntüsü.

Kontrol edildi

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

Bu durum, on durumunu gösterir. Bu durumda, giriş "parça" arka planı etkin renge, başparmak konumu ise "sona" ayarlanır.

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

Devre dışı

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

:disabled düğmesi yalnızca görsel olarak farklı görünmekle kalmaz, aynı zamanda öğeyi değişmez hale getirmelidir.Etkileşim değişmezliği tarayıcıdan bağımsızdır ancak appearance: none kullanımı nedeniyle görsel durumlar için stiller gerekir.

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

Devre dışı, işaretli ve işaretsiz durumlarda koyu stil anahtar.

Bu durum, hem devre dışı bırakılmış hem de işaretlenmiş durumlarla birlikte koyu ve açık temalar gerektirdiğinden karmaşıktır. Stil kombinasyonlarının bakım yükünü azaltmak için bu durumlar için stilistik olarak minimal stiller seçtim.

Belirsiz

Genellikle unutulan bir durum da onay kutusunun işaretli veya işaretsiz olmadığı :indeterminate durumudur. Bu durum eğlenceli, davetkar ve mütevazı. Boole durumlarının, aralarda gizli durumları olabileceğini hatırlatır.

Onay kutusunu belirsiz olarak ayarlamak zordur, yalnızca JavaScript ile ayarlanabilir:

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

Kararsızlığı belirtmek için parça küçük resminin ortada olduğu belirsiz durum.

Durum bana göre mütevazı ve davetkar olduğundan anahtar başparmak konumunu ortaya yerleştirmek uygun olur:

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

İmleçle üzerine gelin

Fareyle üzerine gelme etkileşimleri, bağlı kullanıcı arayüzü için görsel destek sağlamalı ve etkileşimli kullanıcı arayüzüne yönlendirmelidir. Bu anahtar, etiket veya girişin üzerine gelindiğinde yarı şeffaf bir halkayla küçük resmi vurgular. Bu fareyle üzerine gelme animasyonu, etkileşimli küçük resim öğesine yönlendirir.

"Vurgulama" efekti box-shadow ile yapılır. Devre dışı bırakılmamış bir girişin üzerine gelindiğinde --highlight-size simgesinin boyutunu artırın. Kullanıcı hareketle ilgili bir sorun yaşamıyorsa box-shadow geçişini yaparız ve bu geçişin büyümesini izleriz. Kullanıcı hareketle ilgili bir sorun yaşıyorsa öne çıkan an anında gösterilir:

.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

Bana göre, özellikle bir pistin içindeki daire gibi bir anahtar arayüzü, fiziksel bir arayüzü taklit etmeye çalışırken tuhaf bir his veriyor. iOS, anahtarıyla bu işi doğru yaptı. Anahtarları yan yana sürükleyebiliyorsunuz ve bu seçeneğin olması çok tatmin edici. Aksine, bir sürükleme hareketi denenip hiçbir şey olmazsa kullanıcı arayüzü öğesi etkin değilmiş gibi görünebilir.

Sürüklenebilir küçük resimler

Başparmak sözde öğesi, konumunu .gui-switch > input kapsamlı var(--thumb-position) öğesinden alır. JavaScript, başparmak konumunu dinamik olarak güncellemek için girişte satır içi stil değeri sağlayabilir. Böylece başparmak, işaretçi hareketini takip ediyormuş gibi görünür. İşaretçi bırakıldığında satır içi stilleri kaldırın ve --thumb-position özel özelliğini kullanarak sürüklemenin kapalıya mı yoksa açığa mı daha yakın olduğunu belirleyin. Bu, çözümün temelini oluşturur. CSS özel özelliklerini değiştirmek için işaretçi konumlarını koşullu olarak izleyen işaretçi etkinlikleri.

Bileşen, bu komut dosyası gösterilmeden önce zaten% 100 işlevsel olduğundan, mevcut davranışı korumak (ör. girişi değiştirmek için bir etiketi tıklamak) oldukça fazla çalışma gerektirir. JavaScript'imiz, mevcut özelliklerin pahasına özellik eklememelidir.

touch-action

Sürükleme, özel bir hareket olduğundan touch-action avantajları için mükemmel bir adaydır. Bu anahtar için yatay hareket komut dosyamız tarafından işlenmeli veya dikey anahtar varyantı için dikey hareket yakalanmalıdır. touch-action ile tarayıcıya bu öğede hangi hareketlerin işleneceğini söyleyebiliriz. Böylece bir komut dosyası, hareketi rekabet olmadan işleyebilir.

Aşağıdaki CSS, tarayıcıya bir işaretçi hareketi bu anahtar izinin içinden başladığında dikey hareketleri işlemesini, yatay hareketlerle ilgili hiçbir şey yapmamasını söyler:

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

İstenen sonuç, sayfayı kaydırmayan veya sayfada gezinmeyen yatay bir harekettir. İşaretçi, girişte dikey olarak kaydırmaya başlayabilir ve sayfayı kaydırabilir ancak yatay kaydırma özel olarak işlenir.

Piksel değer stili yardımcı programları

Kurulum sırasında ve sürükleme işlemi esnasında, öğelerden çeşitli hesaplanmış sayı değerlerinin alınması gerekir. Aşağıdaki JavaScript işlevleri, bir CSS özelliği verildiğinde hesaplanmış piksel değerlerini döndürür. Kurulum komut dosyasında şu şekilde kullanılır: 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() işlevinin ikinci bir bağımsız değişkeni, hedef sözde öğeyi nasıl kabul ettiğine dikkat edin. JavaScript'in öğelerden, hatta sözde öğelerden bu kadar çok değer okuyabilmesi oldukça kullanışlıdır.

dragging

Bu, sürükleme mantığı için temel bir andır ve işlev etkinlik işleyicisinde dikkat edilmesi gereken birkaç nokta vardır:

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

Komut dosyası kahramanı, bu komut dosyasının bir işaretçiyle birlikte konumlandırdığı küçük daire olan state.activethumb'dır. switches nesnesi, anahtarların .gui-switch olduğu ve değerlerin, komut dosyasının verimli kalmasını sağlayan önbelleğe alınmış sınırlar ve boyutlar olduğu bir Map()'dir. Sağdan sola, CSS'nin --isLTR aynı özel özellik kullanılarak işlenir ve mantığı tersine çevirmek ve sağdan sola dil desteğini sürdürmek için bu özellikten yararlanılabilir. event.offsetX değeri de önemlidir. Çünkü bu değer, başparmağın konumlandırılması için yararlı bir delta değeri içerir.

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

CSS'nin bu son satırı, küçük resim öğesi tarafından kullanılan özel özelliği ayarlar. Bu değer ataması normalde zaman içinde geçiş yapardı ancak önceki bir işaretçi etkinliği, --thumb-transition-duration değerini geçici olarak 0s olarak ayarlayarak yavaş bir etkileşime neden olabilecek durumu ortadan kaldırdı.

dragEnd

Kullanıcının anahtarın çok dışına sürükleyip bırakabilmesi için genel bir pencere etkinliğinin kaydedilmesi gerekiyordu:

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

  dragEnd(event)
})

Kullanıcının serbestçe sürükleme yapabilmesi ve arayüzün bunu hesaba alacak kadar akıllı olması gerektiğini düşünüyorum. Bu anahtarla işlemek çok fazla zaman almadı ancak geliştirme sürecinde dikkatli bir şekilde ele alınması gerekti.

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

Öğeyle etkileşim tamamlandı. Girişin checked özelliğini ayarlamanın ve tüm hareket etkinliklerini kaldırmanın zamanı geldi. Onay kutusu state.activethumb.checked = determineChecked() ile değiştirildi.

determineChecked()

dragEnd tarafından çağrılan bu işlev, mevcut başparmağın parçasının sınırları içinde nerede bulunduğunu belirler ve parçanın yarısına eşit veya yarısından fazla ise true değerini döndürür:

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
}

Ek düşünceler

Sürükleme hareketi, başlangıçta seçilen HTML yapısı nedeniyle biraz kod borcuna yol açtı. Özellikle de girişin bir etiketle sarmalanması bu duruma neden oldu. Bir üst öğe olan etiket, girişten sonra tıklama etkileşimleri alır. dragEnd etkinliğinin sonunda, padRelease()'nin garip bir işlev olduğunu fark etmiş olabilirsiniz.

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

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

Bu, kullanıcının gerçekleştirdiği etkileşimin işaretini kaldıracağı veya işaretleyeceği için etiketin bu sonraki tıklamayı hesaba katması içindir.

Bu işlemi tekrar yapacak olsam, etiketin tıklamalarını kendisi işleyen ve yerleşik davranışla çakışmayan bir öğe oluşturmak için kullanıcı deneyimi yükseltmesi sırasında JavaScript ile DOM'u ayarlamayı düşünebilirim.

Bu tür JavaScript'i yazmayı en az sevdiğim şeydir. Koşullu etkinlik kabarcıklanmasını yönetmek istemiyorum:

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

Sonuç

Bu minik anahtar bileşeni, şimdiye kadarki tüm GUI Challenges'da en çok uğraştıran oldu. Bunu nasıl yaptığımı öğrendiğinize göre, siz nasıl yapardınız? 🙂

Yaklaşımlarımızı çeşitlendirelim ve web'de içerik oluşturmanın tüm yollarını öğrenelim. Bir demo oluşturun, bağlantıları bana tweet atın. Ben de bu bağlantıları aşağıdaki topluluk remiksleri bölümüne ekleyeyim.

Topluluk remiksleri

Kaynaklar

.gui-switch Kaynak kodunu GitHub'da bulabilirsiniz.