סקירה כללית בסיסית על יצירת סרגל טעינה מותאם לצבע ונגיש באמצעות האלמנט <progress>
.
בפוסט הזה אני רוצה לשתף את הדרך שבה אפשר ליצור סרגל טעינה מותאם לצבע ונגיש באמצעות האלמנט <progress>
. רוצים לנסות את הדמו ולעיין במקור?
אם אתם מעדיפים סרטון, הנה גרסה של הפוסט הזה ב-YouTube:
סקירה כללית
האלמנט <progress>
מספק למשתמשים משוב חזותי ושמיעתי על השלמה. משוב חזותי כזה שימושי בתרחישים כמו: התקדמות בטופס, הצגת מידע על הורדה או העלאה, או אפילו הצגת הודעה על כך שסכום ההתקדמות לא ידוע אבל העבודה עדיין פעילה.
באתגר GUI הזה השתמשו ברכיב ה-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 DevTools:
- לוחצים לחיצה ימנית על הדף ובוחרים באפשרות בדיקת המרכיב כדי להציג את כלי הפיתוח.
- לוחצים על גלגל השיניים של ההגדרות בפינה השמאלית העליונה של חלון DevTools.
- מתחת לכותרת Elements, מחפשים את התיבה Show user agent shadow 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;
}
נכסים מותאמים אישית גם יעזרו לשמור על הקוד ללא קוד מיותר, כי שוב, אי אפשר לקבץ יחד את הבוררים הספציפיים לדפדפן.
תמונות המפתח (keyframes)
המטרה היא ליצור אנימציה אינסופית שמתקדמת הלוך ושוב. נקודות ה-keyframe של ההתחלה והסיום יוגדרו ב-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>
בהתאם לסטטוס ההתקדמות, צריך לעדכן את המאפיין aria-busy
של הרכיב <main>
הקשור:
const setProgress = () => {
zone.setAttribute('aria-busy', state.val < 1)
}
איך למחוק את המאפיינים אם סכום הטעינה לא ידוע
אם הערך לא ידוע או לא מוגדר, null
, בשימוש הזה, מסירים את המאפיינים value
ו-aria-valuenow
. הפעולה הזו תהפוך את הערך של <progress>
ל-indeterminate.
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>
. כדאי לבדוק את האפשרויות האלה.
נרחיב את הגישות שלנו ונלמד את כל הדרכים לפיתוח באינטרנט.
אתם יכולים ליצור גרסת דמו, לשלוח לי קישורים בטוויטר ואוסיף אותה לקטע 'רמיקסים של הקהילה' שבהמשך.