نظرة عامة أساسية حول كيفية إنشاء شريط تحميل متوافق مع الألوان وسهل الاستخدام باستخدام العنصر <progress>
في هذه المشاركة، أريد أن أشارك أفكارًا حول كيفية إنشاء شريط تحميل متوافق مع الألوان ويمكن الوصول إليه باستخدام العنصر <progress>
. جرِّب
العرض التوضيحي واطّلِع على
المصدر.
إذا كنت تفضّل مشاهدة فيديو، إليك نسخة من هذا المنشور على YouTube:
نظرة عامة
يوفّر العنصر
<progress>
ملاحظات مرئية ومسموعة للمستخدمين بشأن اكتمال العملية. تُعدّ هذه الملاحظات المرئية مفيدة في سيناريوهات مثل: إحراز تقدّم في نموذج، أو عرض معلومات التنزيل أو التحميل، أو حتى الإشارة إلى أنّ مقدار التقدّم غير معروف ولكن العمل لا يزال نشطًا.
كانت GUI Challenge تعمل مع عنصر HTML <progress>
الحالي لتوفير بعض الجهد في ما يتعلّق بتسهيل الاستخدام. تتجاوز الألوان والتنسيقات حدود التخصيص للعنصر المضمّن، وذلك بهدف تحديث المكوّن وجعله يتناسب بشكل أفضل مع أنظمة التصميم.

Markup
لقد اخترت تضمين العنصر <progress>
في <label>
كي
أتمكّن من تخطّي سمات العلاقة الواضحة واستخدام علاقة ضمنية.
لقد أضفتُ أيضًا تصنيفًا إلى عنصر رئيسي يتأثر بحالة التحميل، حتى تتمكّن تقنيات قراءة الشاشة من نقل هذه المعلومات إلى المستخدم.
<progress></progress>
إذا لم يكن هناك value
، يكون تقدّم العنصر
غير محدّد.
تكون قيمة السمة max
تلقائية هي 1، لذا يتراوح مستوى التقدّم بين 0 و1. سيؤدي ضبط max
على 100، على سبيل المثال، إلى ضبط النطاق على 0-100. لقد اخترت البقاء ضمن الحدود 0 و1، مع تحويل قيم التقدم إلى 0.5 أو %50.
التقدّم المضمّن في التصنيف
في العلاقة الضمنية، يتم تضمين عنصر تقدّم في تصنيف على النحو التالي:
<label>Loading progress<progress></progress></label>
في العرض التوضيحي، اخترت تضمين التصنيف لبرامج قراءة الشاشة فقط.
يتم ذلك من خلال تضمين نص التصنيف في <span>
وتطبيق بعض الأنماط عليه ليكون خارج الشاشة بشكل فعال:
<label>
<span class="sr-only">Loading progress</span>
<progress></progress>
</label>
مع CSS المصاحبة التالية من WebAIM:
.sr-only {
clip: rect(1px, 1px, 1px, 1px);
clip-path: inset(50%);
height: 1px;
width: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
}
المنطقة المتأثّرة بمستوى تقدّم التحميل
إذا كان نظرك سليمًا، قد يسهل عليك ربط مؤشر التقدم بالعناصر ومساحات الصفحة ذات الصلة، ولكن بالنسبة إلى المستخدمين الذين يعانون من ضعف في النظر، لا يكون الأمر واضحًا. يمكنك تحسين ذلك من خلال تعيين السمة
aria-busy
للعنصر العلوي الذي سيتغير عند اكتمال التحميل.
بالإضافة إلى ذلك، يجب الإشارة إلى العلاقة بين مستوى التقدّم ومنطقة التحميل باستخدام
aria-describedby
.
<main id="loading-zone" aria-busy="true">
…
<progress aria-describedby="loading-zone"></progress>
</main>
من JavaScript، بدِّل القيمة من aria-busy
إلى true
في بداية المهمة، وإلى false
عند الانتهاء منها.
إضافة سمات Aria
على الرغم من أنّ الدور الضمني لعنصر <progress>
هو
progressbar
، فقد جعلته دورًا صريحًا
للمتصفحات التي لا يتوفّر فيها هذا الدور الضمني. أضفتُ أيضًا السمة
indeterminate
لوضع العنصر بشكل صريح في حالة غير معروفة، وهو
أكثر وضوحًا من ملاحظة أنّ العنصر لا يتضمّن value
.
<label>
Loading
<progress
indeterminate
role="progressbar"
aria-describedby="loading-zone"
tabindex="-1"
>unknown</progress>
</label>
استخدِم
tabindex="-1"
لإتاحة التركيز على عنصر شريط التقدم من JavaScript. وهذا مهم بالنسبة إلى تكنولوجيا قارئات الشاشة، لأنّ منح التركيز على شريط التقدم عند تغيّر حالة التقدم سيُعلم المستخدم بمقدار التقدم الذي تم إحرازه.
الأنماط
قد يكون عنصر شريط التقدم صعبًا بعض الشيء عندما يتعلق الأمر بتنسيقه. تحتوي عناصر HTML المضمّنة على أجزاء مخفية خاصة يصعب تحديدها، ولا تتيح في الغالب سوى مجموعة محدودة من الخصائص التي يمكن ضبطها.
التنسيق
تهدف أنماط التنسيق إلى توفير بعض المرونة في حجم عنصر التقدم وموضع التصنيف. تمت إضافة حالة اكتمال خاصة يمكن أن تكون إشارة مرئية إضافية مفيدة، ولكنّها ليست مطلوبة.
التنسيق <progress>
لا يتم تغيير عرض عنصر شريط التقدم لكي يتمكن من التوسّع والتقلّص حسب المساحة المطلوبة في التصميم. تتم إزالة الأنماط المضمّنة من خلال ضبط appearance
وborder
على none
. ويتم ذلك حتى يمكن تسوية العنصر على مستوى المتصفحات، لأنّ لكل متصفح أنماطه الخاصة بالعنصر.
progress {
--_track-size: min(10px, 1ex);
--_radius: 1e3px;
/* reset */
appearance: none;
border: none;
position: relative;
height: var(--_track-size);
border-radius: var(--_radius);
overflow: hidden;
}
تستخدم قيمة 1e3px
الخاصة بـ _radius
الترميز العلمي للأرقام للتعبير عن رقم كبير، لذا يتم تقريب border-radius
دائمًا. وهي تعادل
1000px
. أفضّل استخدام هذا الخيار لأنّ هدفي هو استخدام قيمة كبيرة بما يكفي لأتمكّن من ضبطها مرة واحدة وعدم الحاجة إلى تغييرها (كما أنّ كتابتها أسهل من 1000px
). ويمكن أيضًا تكبيرها بسهولة عند الحاجة، وذلك من خلال تغيير الرقم 3 إلى 4، وبالتالي تصبح 1e4px
مكافئة لـ 10000px
.
يتم استخدام overflow: hidden
، وهو نمط مثير للجدل. وقد سهّل هذا التغيير بعض الأمور، مثل عدم الحاجة إلى تمرير قيم border-radius
إلى المسار وعناصر تعبئة المسار، ولكنّه يعني أيضًا أنّه لا يمكن أن تتواجد أي عناصر تابعة لشريط التقدم خارج العنصر. يمكن إجراء تكرار آخر على عنصر مقياس التقدم المخصّص هذا بدون overflow: hidden
، وقد يتيح ذلك بعض الفرص لإنشاء رسوم متحركة أو حالات إكمال أفضل.
اكتمل مستوى التقدم
تتولّى أدوات اختيار CSS مهمة المقارنة بين الحد الأقصى والقيمة، وإذا تطابقا، يكون قد تم إحراز تقدّم. عند اكتمال العملية، يتم إنشاء عنصر زائف وإلحاقه بنهاية عنصر التقدّم، ما يوفّر إشارة مرئية إضافية لطيفة إلى الاكتمال.
progress:not([max])[value="1"]::before,
progress[max="100"][value="100"]::before {
content: "✓";
position: absolute;
inset-block: 0;
inset-inline: auto 0;
display: flex;
align-items: center;
padding-inline-end: max(calc(var(--_track-size) / 4), 3px);
color: white;
font-size: calc(var(--_track-size) / 1.25);
}
اللون
يوفّر المتصفّح ألوانًا خاصة بعنصر شريط التقدّم، وهي تتكيّف مع الوضعين الفاتح والداكن باستخدام سمة واحدة فقط من سمات CSS. ويمكن الاستفادة من ذلك باستخدام بعض أدوات الاختيار الخاصة بالمتصفّح.
أنماط المتصفّح الفاتحة والداكنة
لتفعيل عنصر <progress>
متوافق مع المظهر الداكن والفاتح على موقعك الإلكتروني، كل ما عليك فعله هو إضافة color-scheme
.
progress {
color-scheme: light dark;
}
لون تعبئة مستوى تقدّم الموقع الفردي
لتلوين عنصر <progress>
، استخدِم accent-color
.
progress {
accent-color: rebeccapurple;
}
لاحظ أنّ لون خلفية المقطع الصوتي يتغيّر من فاتح إلى داكن حسب قيمة accent-color
. يضمن المتصفّح التباين المناسب، وهذا أمر رائع.
ألوان فاتحة وداكنة مخصّصة بالكامل
اضبط سمتَين مخصّصتَين على العنصر <progress>
، إحداهما للون المسار والأخرى للون تقدّم المسار. داخل طلب البحث عن الوسائط
prefers-color-scheme
، أدخِل قيم ألوان جديدة للمقطع الصوتي ومستوى تقدّمه.
progress {
--_track: hsl(228 100% 90%);
--_progress: hsl(228 100% 50%);
}
@media (prefers-color-scheme: dark) {
progress {
--_track: hsl(228 20% 30%);
--_progress: hsl(228 100% 75%);
}
}
أنماط التركيز
في وقت سابق، منحنا العنصر فهرس علامات تبويب سالبًا حتى يمكن التركيز عليه آليًا. استخدِم
:focus-visible
لتخصيص حلقة التركيز من أجل تفعيل نمط حلقة التركيز الأذكى. في هذه الحالة، لن تؤدي نقرة الماوس والتركيز إلى عرض حلقة التركيز، ولكن ستؤدي نقرات لوحة المفاتيح إلى ذلك. يتناول
فيديو YouTube هذا الموضوع بتفصيل أكبر
ويستحق المراجعة.
progress:focus-visible {
outline-color: var(--_progress);
outline-offset: 5px;
}
الأنماط المخصّصة على جميع المتصفّحات
خصِّص الأنماط من خلال اختيار أجزاء عنصر <progress>
التي يعرضها كل متصفّح. استخدام عنصر التقدم هو علامة واحدة، ولكنها تتألف من بعض العناصر الفرعية التي يتم عرضها من خلال أدوات اختيار CSS الصورية. ستعرض لك "أدوات مطوّري البرامج في Chrome" هذه العناصر إذا فعّلت الإعداد التالي:
- انقر بزر الماوس الأيمن على صفحتك واختَر فحص العنصر لعرض "أدوات مطوّري البرامج".
- انقر على رمز ترس الإعدادات في أعلى يسار نافذة "أدوات مطوّلي البرامج".
- ضمن العنوان العناصر، ابحث عن مربّع الاختيار عرض ظل وكيل المستخدم DOM وفعِّله.
أنماط Safari وChromium
تتيح المتصفحات المستندة إلى WebKit، مثل Safari وChromium، استخدام ::-webkit-progress-bar
و::-webkit-progress-value
، ما يسمح باستخدام مجموعة فرعية من CSS. في الوقت الحالي، اضبط background-color
باستخدام الخصائص المخصّصة
التي تم إنشاؤها سابقًا، والتي تتكيّف مع الوضعين الفاتح والداكن.
/* Safari/Chromium */
progress[value]::-webkit-progress-bar {
background-color: var(--_track);
}
progress[value]::-webkit-progress-value {
background-color: var(--_progress);
}
أنماط Firefox
لا يعرض Firefox أداة الاختيار الزائفة ::-moz-progress-bar
إلا على العنصر <progress>
. وهذا يعني أيضًا أنّه لا يمكننا تلوين المسار مباشرةً.
/* Firefox */
progress[value]::-moz-progress-bar {
background-color: var(--_progress);
}
لاحظ أنّ Firefox ضبط لون المسار من accent-color
بينما يضبط iOS Safari لون المسار على الأزرق الفاتح. ويحدث الأمر نفسه في الوضع الداكن: يعرض Firefox شريطًا داكنًا ولكن ليس باللون المخصّص الذي اخترناه، ويعمل في المتصفّحات المستندة إلى Webkit.
Animation
عند استخدام أدوات اختيار العناصر الصورية المضمّنة في المتصفح، غالبًا ما يكون ذلك مع مجموعة محدودة من خصائص CSS المسموح بها.
تحريك عملية ملء المسار
تعمل إضافة انتقال إلى
inline-size
لعنصر التقدم في Chromium، ولكن ليس في Safari. لا يستخدم Firefox أيضًا سمة انتقالية على ::-moz-progress-bar
.
/* Chromium Only 😢 */
progress[value]::-webkit-progress-value {
background-color: var(--_progress);
transition: inline-size .25s ease-out;
}
تحريك الحالة :indeterminate
أحاول هنا أن أكون أكثر إبداعًا حتى أتمكّن من تقديم صورة متحركة. يتم إنشاء عنصر زائف لـ Chromium ويتم تطبيق تدرّج يتم تحريكه ذهابًا وإيابًا لجميع المتصفحات الثلاثة.
الخصائص المخصّصة
تُعدّ الخصائص المخصّصة رائعة في العديد من الحالات، ولكن إحدى الميزات المفضّلة لديّ هي ببساطة
تسمية قيمة CSS تبدو سحرية. في ما يلي linear-gradient
معقّد
إلى حد ما
يحمل اسمًا لطيفًا. يمكن فهم الغرض منه وحالات استخدامه بوضوح.
progress {
--_indeterminate-track: linear-gradient(to right,
var(--_track) 45%,
var(--_progress) 0%,
var(--_progress) 55%,
var(--_track) 0%
);
--_indeterminate-track-size: 225% 100%;
--_indeterminate-track-animation: progress-loading 2s infinite ease;
}
ستساعد الخصائص المخصّصة أيضًا في الحفاظ على رمز DRY، لأنّه مرة أخرى، لا يمكننا تجميع أدوات الاختيار الخاصة بالمتصفح معًا.
الإطارات الرئيسية
الهدف هو إنشاء صورة متحركة بلا نهاية تتكرّر ذهابًا وإيابًا. سيتم ضبط إطارات المفاتيح الخاصة بالبدء والانتهاء في CSS. ما عليك سوى استخدام إطار مفتاح واحد، وهو إطار المفتاح الأوسط عند 50%
، لإنشاء صورة متحركة تعود إلى نقطة البداية بشكل متكرر.
@keyframes progress-loading {
50% {
background-position: left;
}
}
استهداف كل متصفّح
لا يسمح كل متصفّح بإنشاء عناصر زائفة على عنصر <progress>
نفسه أو بتحريك شريط التقدم. تتيح المزيد من المتصفّحات تحريك المسار مقارنةً بالعنصر الزائف، لذا أفضّل استخدام العناصر الزائفة كأساس ثم تحريك الأشرطة.
العنصر الزائف Chromium
يسمح Chromium بالعنصر الزائف: ::after
المستخدَم مع موضع لتغطية العنصر. يتم استخدام الخصائص المخصّصة غير المحدّدة، وتعمل الحركة ذهابًا وإيابًا بشكل جيد جدًا.
progress:indeterminate::after {
content: "";
inset: 0;
position: absolute;
background: var(--_indeterminate-track);
background-size: var(--_indeterminate-track-size);
background-position: right;
animation: var(--_indeterminate-track-animation);
}
شريط التقدّم في Safari
في Safari، يتم تطبيق الخصائص المخصّصة والصورة المتحركة على شريط التقدم الخاص بالعنصر الزائف:
progress:indeterminate::-webkit-progress-bar {
background: var(--_indeterminate-track);
background-size: var(--_indeterminate-track-size);
background-position: right;
animation: var(--_indeterminate-track-animation);
}
شريط التقدم في Firefox
في Firefox، يتم أيضًا تطبيق الخصائص المخصّصة والحركة على شريط التقدّم الخاص بالعنصر الزائف:
progress:indeterminate::-moz-progress-bar {
background: var(--_indeterminate-track);
background-size: var(--_indeterminate-track-size);
background-position: right;
animation: var(--_indeterminate-track-animation);
}
JavaScript
تؤدي JavaScript دورًا مهمًا مع العنصر <progress>
. تتحكّم هذه السمة في القيمة التي يتم إرسالها إلى العنصر وتضمن توفّر معلومات كافية في المستند لبرامج قراءة الشاشة.
const state = {
val: null
}
يوفّر العرض التوضيحي أزرارًا للتحكّم في مستوى التقدّم، وهي تعدّل state.val
ثم تستدعي دالة لتعديل
DOM.
document.querySelector('#complete').addEventListener('click', e => {
state.val = 1
setProgress()
})
setProgress()
هذه الدالة هي المكان الذي يتم فيه تنسيق واجهة المستخدم وتجربة المستخدم. ابدأ بإنشاء
setProgress()
دالة. لا حاجة إلى مَعلمات لأنّها تملك إذن الوصول إلى العنصر state
وعنصر التقدّم والمنطقة <main>
.
const setProgress = () => {
}
ضبط حالة التحميل على المنطقة <main>
اعتمادًا على ما إذا كان التقدم مكتملًا أم لا، يجب تعديل عنصر <main>
ذي الصلة في السمة aria-busy
:
const setProgress = () => {
zone.setAttribute('aria-busy', state.val < 1)
}
محو السمات إذا كانت قيمة التحميل غير معروفة
إذا كانت القيمة غير معروفة أو لم يتم ضبطها، null
في هذا الاستخدام، أزِل السمتَين value
وaria-valuenow
. سيؤدي ذلك إلى ضبط حالة <progress>
على "غير محدّد".
const setProgress = () => {
zone.setAttribute('aria-busy', state.val < 1)
if (state.val === null) {
progress.removeAttribute('aria-valuenow')
progress.removeAttribute('value')
progress.focus()
return
}
}
حلّ مشاكل العمليات الحسابية العشرية في JavaScript
بما أنّني اخترت الالتزام بالحد الأقصى التلقائي للتقدّم وهو 1، تستخدم دالتا الزيادة والنقصان في العرض التوضيحي العمليات الحسابية العشرية. لا تكون JavaScript واللغات الأخرى فعّالة دائمًا في ذلك.
إليك دالة roundDecimals()
تعمل على إزالة الأرقام الزائدة من نتيجة العملية الحسابية:
const roundDecimals = (val, places) =>
+(Math.round(val + "e+" + places) + "e-" + places)
قرِّب القيمة لتتمكّن من عرضها وقراءتها:
const setProgress = () => {
zone.setAttribute('aria-busy', state.val < 1)
if (state.val === null) {
progress.removeAttribute('aria-valuenow')
progress.removeAttribute('value')
progress.focus()
return
}
const val = roundDecimals(state.val, 2)
const valPercent = val * 100 + "%"
}
ضبط قيمة لبرامج قراءة الشاشة وحالة المتصفّح
يتم استخدام القيمة في ثلاثة مواضع في نموذج المستند (DOM):
- السمة
value
للعنصر<progress>
- السمة
aria-valuenow
- محتوى النص الداخلي
<progress>
const setProgress = () => {
zone.setAttribute('aria-busy', state.val < 1)
if (state.val === null) {
progress.removeAttribute('aria-valuenow')
progress.removeAttribute('value')
progress.focus()
return
}
const val = roundDecimals(state.val, 2)
const valPercent = val * 100 + "%"
progress.value = val
progress.setAttribute('aria-valuenow', valPercent)
progress.innerText = valPercent
}
منح التركيز على شريط التقدّم
بعد تعديل القيم، سيلاحظ المستخدمون المبصرون التغيير في مستوى التقدّم، ولكن لن يتم إعلام مستخدمي قارئات الشاشة بالتغيير بعد. ركِّز على العنصر <progress>
وسيُعلن المتصفّح عن التعديل.
const setProgress = () => {
zone.setAttribute('aria-busy', state.val < 1)
if (state.val === null) {
progress.removeAttribute('aria-valuenow')
progress.removeAttribute('value')
progress.focus()
return
}
const val = roundDecimals(state.val, 2)
const valPercent = val * 100 + "%"
progress.value = val
progress.setAttribute('aria-valuenow', valPercent)
progress.innerText = valPercent
progress.focus()
}
الخاتمة
بعد أن عرفت كيف فعلتُ ذلك، كيف ستفعل أنت ذلك؟ 🙂
بالتأكيد، هناك بعض التغييرات التي أودّ إجراءها إذا أتيحت لي فرصة أخرى. أعتقد أنّه يمكن تحسين المكوّن الحالي، كما يمكن محاولة إنشاء مكوّن بدون قيود نمط الفئة الزائفة للعنصر <progress>
. ننصحك بتجربتها.
لنستكشف الطرق المختلفة لإنشاء مواقع إلكترونية على الويب.
يمكنك إنشاء عرض توضيحي، إرسال تغريدة إليّ تتضمّن رابطًا إليه، وسأضيفه إلى قسم "ريمكسات من المنتدى" أدناه.