نظرة عامة أساسية حول كيفية إنشاء مكوّن تبديل متجاوب وسهل الاستخدام
في هذه المنشور، أرغب في مشاركة التفكير في طريقة لإنشاء مكونات التبديل. جرِّب العرض التجريبي.
إذا كنت تفضّل الفيديو، يمكنك الاطّلاع على نسخة من هذه المشاركة على YouTube:
نظرة عامة
يعمل switch بشكل مشابه لمربع اختيار، ولكنه يمثل بشكل صريح حالات التشغيل والإيقاف المنطقي.
يستخدم هذا العرض التوضيحي <input type="checkbox" role="switch">
في معظم وظائفه، وهي ميزة تعتمد على عدم الحاجة إلى CSS أو JavaScript كي يعمل بشكل كامل ويمكن الوصول إليه. يوفر تحميل CSS دعمًا للغات التي تبدأ من اليمين إلى اليسار،
والرأسية، والرسوم المتحركة والمزيد. يؤدي تحميل JavaScript إلى جعل مفتاح التبديل قابلاً للتحريك واللمس.
الخصائص المخصّصة
تمثّل المتغيّرات التالية الأجزاء المختلفة للتبديل
وخياراتها. بصفتها فئة المستوى الأعلى، تحتوي .gui-switch
على خصائص مخصّصة يتم استخدامها
في جميع العناصر الفرعية للمكونات، ونقاط دخول للقيام بعملية تخصيص
مركزية.
مسار
الطول (--track-size
) والتباعد واللونان:
.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%);
}
}
الحدّ من الحركة
لإضافة عنوان بديل واضح وتقليل التكرار، يمكن وضع طلب ملف شخصي وسائط يفضّل استخدام الصور ذات الحركة المنخفضة في موقع مخصّص باستخدام مكوّن PostCSS الإضافي استنادًا إلى مسودة المواصفات في طلبات ملفات الوسائط 5:
@custom-media --motionOK (prefers-reduced-motion: no-preference);
Markup
اختَرتُ لفّ عنصر <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">
مُعدّ مسبقًا باستخدام
واجهة برمجة تطبيقات
وحالة. يدير المتصفّح
checked
الخاصية وأحداث
الإدخال
مثل oninput
وonchanged
.
التنسيقات
إنّ Flexbox وgrid وسمات المخصّصة مهمة للحفاظ على أنماط هذا المكوّن. فهي تركز على القيم، وتعطي أسماء لحسابات أو مناطق غامضة، وتمكن واجهة برمجة تطبيقات صغيرة مخصصة للخصائص المخصصة لإجراء عمليات تخصيص سهلة للمكونات.
.gui-switch
تخطيط المستوى الأعلى للمبدل هو flexbox. تحتوي الفئة .gui-switch
على
السمات المخصّصة الخاصة والعامة التي يستخدمها الأطفال لاحتساب
التنسيقات.
.gui-switch {
display: flex;
align-items: center;
gap: 2ch;
justify-content: space-between;
}
إنّ توسيع تنسيق المربّع المرن وتعديله يشبه تغيير أي تنسيق مربّع مرن.
على سبيل المثال، لوضع تصنيفات فوق مفتاح تبديل أو تحته، أو لتغيير
flex-direction
:
<label for="light-switch" class="gui-switch" style="flex-direction: column">
Default
<input type="checkbox" role="switch" id="light-switch">
</label>
مسار
يتم تصميم مربّع الاختيار كمسار تبديل من خلال إزالة appearance: checkbox
العادي وتقديم حجمه الخاص بدلاً من ذلك:
.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;
}
ينشئ المسار أيضًا منطقة مسار شبكة خلية واحدة تلو الأخرى للإبهام للمطالبة بها.
صورة مصغرة
يزيل النمط appearance: none
أيضًا علامة الاختيار المرئية التي يوفّرها browser. يستخدم هذا المكوِّن عنصرًا زائفًا و:checked
الفئة الصورية لاستبدال هذا المؤشر المرئي.
مؤشر التقدم هو عنصر زائف تابع لعنصر input[type="checkbox"]
ويتم تجميعه فوق المقطع الصوتي بدلاً من تحته من خلال الاستيلاء على مساحة الشبكة
track
:
.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);
}
تأتي مجموعة كبيرة من خيارات التخصيص لمسار التبديل من أربع
خصائص مخصّصة. تتم إضافة border: none
لأنّ سياسة appearance: none
لا تزيل الحدود من مربّع الاختيار في جميع المتصفّحات.
صورة مصغرة
عنصر الإبهام موجود على اليمين track
ولكنّه يحتاج إلى أنماط الدوائر:
.gui-switch > input::before {
background: var(--thumb-color);
border-radius: 50%;
}
التفاعل
استخدِم السمات المخصّصة للاستعداد للتفاعلات التي ستُظهر تمييزات عند التمرير فوق العناصر وتغييرات موضع إصبعَي الإبهام. يتم أيضًا التحقّق من إعدادات المستخدم المفضّلة قبل الانتقال إلى انماط تمييز العناصر أثناء التمرير أو أثناء الحركة.
.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;
}}
}
موضع الإبهام
توفر الخصائص المخصصة آلية مصدر واحدة لتحديد موضع الإبهام في المسار. يتوفّر لدينا أحجام المقطع الصوتي والملصق التي سنستخدمها في
الحسابات للحفاظ على وضع الملصق بشكل صحيح بين يمين ويسار المقطع الصوتي:
0%
و100%
.
يملك العنصر input
متغير الموضع --thumb-position
، ويستخدمه عنصر الإبهام الزائف كموضع translateX
:
.gui-switch > input {
--thumb-position: 0%;
}
.gui-switch > input::before {
transform: translateX(var(--thumb-position));
}
أصبح بإمكاننا الآن تغيير --thumb-position
من CSS والفئات الصورية
المقدَّمة في عناصر مربّعات الاختيار. بما أنّنا ضبطنا 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)
);
}
اعتقدت أن هذه التوليفة المنفصلة تسير على ما يرام. لا يهتم عنصر الصورة المصغّرة سوى بنمط واحد، وهو موضع translateX
. يمكن للمدخل إدارة جميع
التعقيدات والعمليات الحسابية.
موضع الإعلان
تم إجراء عملية التحسين باستخدام فئة معدِّل -vertical
التي تضيف دورانًا باستخدام
عمليات تحويل CSS إلى عنصر input
.
بالرغم من ذلك، لا يؤدي العنصر الذي يتم تدويره ثلاثي الأبعاد إلى تغيير الارتفاع الإجمالي للمكون،
مما قد يؤدي إلى اختفاء تخطيط الكتلة. يمكنك مراعاة ذلك باستخدام المتغيّرين --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، إيلاد شيكتر، قائمة جانبية جانبية للشرائح باستخدام تحويلات CSS التي تعاملت مع اللغات المكتوبة من اليمين إلى اليسار من خلال قلب متغير واحد. لقد فعلنا ذلك لأنّه لا تتوفّر عمليات تحويل منطقية للخصائص في CSS، وقد لا تتوفّر أبدًا. كانت لدى إلاد فكرة رائعة باستخدام قيمة خاصية مخصصة لعكس النسب المئوية، للسماح بإدارة موقع واحد لمنطقنا المخصص للتحويلات المنطقية. لقد استخدمتُ هذه الطريقة نفسها في عملية التبديل هذه وأعتقد أنّه تم تنفيذها بنجاح:
.gui-switch {
--isLTR: 1;
&:dir(rtl) {
--isLTR: -1;
}
}
تحتوي السمة المخصّصة التي تحمل اسم --isLTR
في البداية على قيمة 1
، ما يعني أنّها true
لأنّ تنسيقنا يكون من اليسار إلى اليمين تلقائيًا. بعد ذلك، باستخدام فئة CSS
الزائفة :dir()
،
يتم ضبط القيمة على -1
عندما يكون المكوّن ضمن تنسيق من اليمين إلى اليسار.
يمكنك استخدام --isLTR
من خلال تضمينها في calc()
داخل عملية تحويل:
.gui-switch.-vertical > input {
transform: rotate(-90deg);
transform: rotate(calc(90deg * var(--isLTR) * -1));
}
يراعي الآن دوران مفتاح التبديل العمودي موضع الجانب المقابل الذي يتطلّبه التنسيق من اليمين إلى اليسار.
يجب أيضًا تعديل عمليات التحويل 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:
محدد
<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
التي غالبًا ما يتم تجاهلها هي الحالة التي لا يكون فيها مربّع الاختيار
محدَّدًا أو غير محدَّد. هذه حالة ممتعة وجذابة ومتواضعة. نذكّرك بأنّه يمكن أن تتضمّن الحالات المنطقية حالات غامضة بين الحالات.
من الصعب ضبط مربّع اختيار على "غير محدّد"، ولا يمكن ضبطه إلا باستخدام 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>
بما أنّ الحالة تبدو لي متواضعة وداعمة، رأيت أنّه من المناسب وضع موضع إصبع الإبهام للتبديل في المنتصف:
.gui-switch > input:indeterminate {
--thumb-position: calc(
calc(calc(var(--track-size) / 2) - calc(var(--thumb-size) / 2))
* var(--isLTR)
);
}
تمرير مؤشر الماوس
يجب أن توفر تفاعلات تمرير الماوس دعمًا مرئيًا لواجهة المستخدم المرتبطة وتوفر أيضًا توجيهات نحو واجهة المستخدم التفاعلية. يُبرز هذا المفتاح الإبهام باستخدام حلقة شبه شفافة عند تمرير مؤشر الماوس فوق التصنيف أو الإدخال. يقدّم هذا المؤثر المتحرك عند التمرير فوق العنصر توجيهًا نحو عنصر الصورة المصغّرة التفاعلي.
يتم تطبيق تأثير "التمييز" باستخدام 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 هذا الأمر بشكلٍ جيد باستخدام مفتاح التبديل، حيث يمكنك سحبه من جانب إلى آخر، ويُسعدك امتلاك هذا الخيار. وعلى العكس من ذلك، يمكن أن يبدو عنصر واجهة المستخدم غير نشط إذا تمت محاولة إيماءة السحب ولم يحدث شيء.
الإبهام القابل للسحب
يتلقّى العنصر النائب للمؤشر موضعَه من .gui-switch > input
var(--thumb-position)
الذي يحدّد النطاق، ويمكن أن تقدّم JavaScript قيمة نمط مضمّنة في
الإدخال لتعديل موضع المؤشر ديناميكيًا بحيث يبدو أنّه يتّبع
إيماءة المؤشر. عند إزالة المؤشر، أزِل الأنماط المضمّنة وحدد ما إذا كان السحب أقرب إلى إيقاف أو تشغيل باستخدام السمة المخصّصة
--thumb-position
. هذا هو العمود الفقري للحلّ، وهو أحداث المؤشر
التي تتبّع بشكل مشروط مواضع المؤشر لتعديل السمات المخصّصة لتنسيق CSS.
ونظرًا لأن المكون كان يعمل بنسبة 100% قبل عرض هذا النص البرمجي، يتطلب الأمر الكثير من العمل للحفاظ على السلوك الحالي، مثل النقر على تصنيف لتبديل الإدخال. يجب ألا تضيف JavaScript ميزات على حساب الميزات الحالية.
touch-action
السحب هو إيماءة مخصّصة، ما يجعله مرشحًا رائعًا للاستفادة من مزايا
touch-action
. في حال هذا التبديل، يجب أن يعالج النص البرمجي لدينا إيماءة أفقية، أو أن يتم تسجيل إيماءة عمودية لإصدار التبديل العمودي. باستخدام touch-action
، يمكننا إخبار المتصفّح بالإيماءات التي يجب معالجتها في
هذا العنصر، حتى يتمكّن نص برمجي من معالجة إيماءة بدون تداخل.
يوجّه ملف CSS التالي المتصفّح إلى أنّه عند بدء إيماءة مؤشر من داخل مسار التبديل هذا، يجب معالجة الإيماءات الرأسية وعدم اتّخاذ أي إجراء بشأن الإيماءات الأفقية:
.gui-switch > input {
touch-action: pan-y;
}
النتيجة المطلوبة هي إيماءة أفقية لا تؤدي أيضًا إلى تحريك الصفحة أو تمريرها. يمكن أن يبدأ المؤشر الانتقال عموديًا من داخل الإدخال وينتقل إلى الصفحة، ولكن يتم التعامل مع المؤشرات الأفقية بشكل مخصّص.
أدوات تنسيق قيم وحدات البكسل
عند الإعداد وأثناء السحب، يجب الحصول على قيم أرقام مختلفة تم احتسابها
من العناصر. تعرض دوال JavaScript التالية قيمًا محسوبة للبكسل
استنادًا إلى سمة 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
، ويمكن استخدامها لعكس المنطق ومواصلة
إتاحة النص من اليمين إلى اليسار. يُعدّ event.offsetX
مفيدًا أيضًا، لأنّه يحتوي على قيمة
فرقة مفيدة لتحديد موضع الإبهام.
state.activethumb.style.setProperty('--thumb-position', `${track + pos}px`)
يضبط هذا السطر الأخير من CSS السمة المخصّصة المستخدَمة من قِبل عنصر Thumb. كان من الممكن أن يتمّ تبديل عملية تحديد القيمة هذه بمرور الوقت، ولكنّ حدث مؤشر سابق حدد مؤقتًا --thumb-transition-duration
على 0s
، ما أدى إلى إزالة ما كان يمكن أن يكون تفاعلًا بطيئًا.
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
موضع إصبع الإبهام الحالي
ضمن حدود مساره وتعرض القيمة 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)
}
يُعدّ ذلك لتوضيح أنّ التصنيف يتلقّى هذا النقر لاحقًا، لأنّه سيلغي وضع علامة في المربّع بجانب التفاعل الذي أجراه المستخدِم أو سيضع علامة فيه.
إذا أردت إجراء ذلك مجددًا، قد أفكّر في تعديل نموذج كائن المستند (DOM) باستخدام JavaScript أثناء ترقية تجربة المستخدم لإنشاء عنصر يعالج نقرات التصنيف نفسها ولا يتعارض مع السلوك المضمّن.
هذا النوع من JavaScript هو الأقلّ تفضيلًا بالنسبة إليّ، ولا أريد إدارة تدفّق الأحداث الشَرطية:
const preventBubbles = event => {
if (state.recentlyDragged)
event.preventDefault() && event.stopPropagation()
}
الخاتمة
لقد كان مكوّن التبديل الصغير هذا هو الأكثر استهلاكًا للوقت من بين جميع تحدّيات واجهتها في واجهة المستخدم حتى الآن. الآن بعد أن عرفت كيف فعلت ذلك، كيف ستفعل ذلك؟ 🙂
لننوّع أساليبنا ونتعرّف على جميع الطرق لإنشاء تطبيقات على الويب. أنشئ عرضًا توضيحيًا أو روابط تغريدةني وسأضيفه إلى قسم الريمكسات في المنتدى أدناه.
الريمكسات التي أنشأها المستخدمون
- @KonstantinRouda باستخدام عنصر مخصّص: demo وcode.
- @jhvanderschee مع زر: Codepen.
الموارد
يمكنك العثور على .gui-switch
الرمز المصدر على
GitHub.