تقدّم هذه الصفحة نظرة عامة أساسية حول طريقة إنشاء شريط تحميل متكيف اللون وسهل الاستخدام باستخدام العنصر <progress>
.
في هذه المشاركة، أريد أن أشارك أفكاري حول كيفية إنشاء شريط تحميل قابل للتكيّف مع الألوان وسهل الاستخدام باستخدام العنصر <progress>
. جرِّب الإصدار التمهيدي واطّلِع على ملف المصدر.
إذا كنت تفضّل الفيديو، يمكنك الاطّلاع على نسخة من هذه المشاركة على YouTube:
نظرة عامة
يوفّر عنصر
<progress>
ملاحظات مرئية وصوتية للمستخدمين حول الإكمال. هذه الملاحظات المرئية مفيدة في سيناريوهات مثل: مستوى التقدّم في ملء نموذج،
عرض معلومات حول تنزيل أو تحميل ملف، أو حتى الإشارة إلى أنّ مقدار التقدّم غير معروف ولكنّ العمل لا يزال نشطًا.
تم حلّ تحدّي واجهة المستخدم هذا باستخدام
عنصر 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;
}
ستساعد السمات المخصّصة أيضًا في إبقاء الرمز البرمجي غير مكتمل، لأنّه لا يمكننا مرة أخرى تجميع أدوات الاختيار الخاصة بالمتصفِّح هذه معًا.
الإطارات الرئيسية
الهدف هو رسم متحرك غير محدود يتنقل ذهابًا وإيابًا. سيتم ضبط اللقطات المفتاح
للبدء والانتهاء في 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>
. ننصحك باستكشافها.
لننوّع أساليبنا ونتعرّف على جميع الطرق لإنشاء تطبيقات على الويب.
أنشئ عرضًا توضيحيًا وأرسِل إلينا رابطًا على Twitter، وسنضيفه إلى قسم الريمكسات التي أنشأها المستخدمون أدناه.