Podstawowe informacje o tym, jak utworzyć pasek wczytywania z adaptacją kolorów i dostępnością za pomocą elementu <progress>
.
W tym poście przedstawię pomysły na stworzenie paska wczytywania z adaptacją kolorów i elementem <progress>
. Wypróbuj
wersję demonstracyjną i wyświetl źródło.
Jeśli wolisz film, oto wersja tego posta na YouTube:
Przegląd
<progress>
dostarcza użytkownikom wizualne i dźwiękowe informacje o ukończeniu procesu. Takie wizualne informacje zwrotne są przydatne w takich sytuacjach, jak: postęp w wypełnianiu formularza, wyświetlanie informacji o pobieraniu lub przesyłaniu danych, a nawet pokazanie, że postęp jest nieznany, ale prace są nadal aktywne.
To wyzwanie GUI współpracowało z istniejącym elementem HTML <progress>
, by zmniejszyć ułatwienia dostępu. Kolory i układy przesuwają granice możliwości dostosowywania wbudowanego elementu, aby zmodernizować komponent i lepiej dopasować go do systemów projektowania.
Markup
Zdecydowałem się uchwycić element <progress>
obiektem <label>
, więc mogę pominąć atrybuty konkretnych relacji na rzecz niejawnej relacji.
Oznaczyłem też element nadrzędny zależny od stanu wczytywania, aby technologie czytników ekranu mogły przekazać te informacje użytkownikowi.
<progress></progress>
Jeśli nie ma parametru value
, postęp działania elementu jest nieokreślony.
Atrybut max
przyjmuje domyślnie wartość 1, więc postęp mieści się w zakresie od 0 do 1. Na przykład ustawienie max
na 100 spowodowałoby ustawienie zakresu na 0–100. Wolę trzymać się limitów 0 i 1,
tłumacząc wartości postępu na 0,5 lub 50%.
Postęp dodany przez etykietę
W relacji niejawnej element postępu jest oznaczony etykietą podobną do tej:
<label>Loading progress<progress></progress></label>
W mojej demonstracji chcę dodać etykietę tylko dla czytników ekranu.
W tym celu trzeba owinąć tekst etykiety w elemencie <span>
i zastosować do niego pewne style, dzięki czemu będzie on widoczny poza ekranem:
<label>
<span class="sr-only">Loading progress</span>
<progress></progress>
</label>
Użyj tego towarzyszącego kodu CSS z 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;
}
Obszar, na którym zależy postęp wczytywania
Jeśli masz dobry wzrok, możesz z łatwością powiązać wskaźnik postępu z powiązanymi elementami i obszarami strony, ale dla użytkowników z wadą wzroku nie jest to tak oczywiste. Aby to poprawić, przypisz atrybut aria-busy
do najwyższego elementu, który zmieni się po zakończeniu wczytywania.
Dodatkowo wskaż za pomocą aria-describedby
związek między postępem a strefą wczytywania.
<main id="loading-zone" aria-busy="true">
…
<progress aria-describedby="loading-zone"></progress>
</main>
Z JavaScriptu przełącz aria-busy
na true
na początku zadania i na false
po jego zakończeniu.
Dodane atrybuty Aria
Choć domyślnie rolę elementu <progress>
pełnią progressbar
, ta rola została wyraźnie wskazana w przeglądarkach, które nie są jej pozbawione. Dodaję też atrybut indeterminate
, aby bezpośrednio umieścić element w nieznanym stanie. Jest to łatwiejsze niż obserwacja elementu, bez ustawionego parametru value
.
<label>
Loading
<progress
indeterminate
role="progressbar"
aria-describedby="loading-zone"
tabindex="-1"
>unknown</progress>
</label>
Użyj tabindex="-1"
, aby umożliwić zaznaczenie elementu postępu z poziomu JavaScriptu. Jest to ważne w przypadku technologii czytnika ekranu, ponieważ informacja o postępie będzie informować użytkownika o tym, jak daleko został osiągnięty zaktualizowany postęp.
Style
Element postępu jest nieco skomplikowany, jeśli chodzi o styl. Wbudowane elementy HTML mają specjalne ukryte części, które trudno wybrać. Często oferują tylko ograniczony zestaw właściwości, które można ustawić.
Układ
Style układu mają zapewniać pewną elastyczność w zakresie rozmiaru i pozycji etykiety elementu postępu. Dodajemy specjalny stan ukończenia, który może być przydatny, ale nie wymagany, dodatkowy wskazówka graficzna.
Układ: <progress>
Szerokość elementu postępu pozostaje niezmieniona, dzięki czemu może się zmniejszać i zwiększać odpowiednio do ilości potrzebnej przestrzeni. Style wbudowane są usuwane przez ustawienie appearance
i border
na none
. Dzięki temu można je znormalizować w różnych przeglądarkach, ponieważ każda przeglądarka ma własny styl.
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;
}
Wartość 1e3px
dla _radius
wykorzystuje notację z liczbą naukową do wyrażenia dużej liczby, dzięki czemu border-radius
jest zawsze zaokrąglana. Jest to odpowiednik 1000px
. Używam tej wartości, ponieważ moim celem jest użycie na tyle dużej wartości, że mogę ją ustawić i ją zapomnieć (i jest krótsza niż w przypadku 1000px
). W razie potrzeby łatwo jest ją zwiększyć – wystarczy zmienić 3 na 4, a 1e4px
odpowiada wartości 10000px
.
Język overflow: hidden
jest używany i jego celem jest kontrowersyjny. Ułatwiono w ten sposób kilka usprawnień, takich jak brak konieczności przekazywania wartości border-radius
do ścieżki i śledzenie elementów wypełnienia. Oznaczało to jednak, że żadne elementy podrzędne postępu nie mogą znajdować się poza tym elementem. Kolejną iterację tego niestandardowego elementu postępu można wykonać bez elementu overflow: hidden
, co mogłoby stworzyć możliwości tworzenia animacji i lepszych stanów ukończenia.
Gotowe
Ciężką pracę wykonuje tu selektory arkusza CSS, porównując wartość maksymalną z wartością. Jeśli do siebie pasują, cały proces się kończy. Po zakończeniu generuje się pseudoelement, który jest dołączany na końcu elementu postępu, zapewniając piękną dodatkową wskazówkę, jak to zrobić.
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);
}
Kolor
Przeglądarka ma własne kolory elementu postępu i może dostosować się do jasnego i ciemnego za pomocą jednej właściwości CSS. Można to wykorzystać przy użyciu specjalnych selektorów właściwych dla poszczególnych przeglądarek.
Jasny i ciemny styl przeglądarki
Aby włączyć w witrynie ciemny i jasny adaptacyjny element <progress>
, wystarczy tylko color-scheme
.
progress {
color-scheme: light dark;
}
Kolor pola postępu w pojedynczej usłudze
Aby zabarwić element <progress>
, użyj właściwości accent-color
.
progress {
accent-color: rebeccapurple;
}
Zwróć uwagę, że kolor tła ścieżki zmienia się z jasnego na ciemny w zależności od parametru accent-color
. Przeglądarka zapewnia odpowiedni kontrast: całkiem schludny.
Całkowicie niestandardowe jasne i ciemne kolory
Ustaw w elemencie <progress>
dwie właściwości niestandardowe – jedną dla koloru ścieżki, a drugą – kolor postępu ścieżki. W zapytaniu o media prefers-color-scheme
podaj nowe wartości kolorów dla ścieżki i postępu.
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%);
}
}
Style ostrości
Wcześniej nadaliśmy elementowi ujemny indeks tabulacji, aby umożliwić jego automatyczne zaznaczenie. Użyj :focus-visible
, aby dostosować ostrość i włączyć inteligentny styl pierścienia ostrości. Po kliknięciu i zaznaczeniu myszą
nie będzie widać pierścienia ostrości, a kliknięcia na klawiaturze. Szczegółowo omawiamy to w filmie w YouTube, który zawiera omówienie tej kwestii.
progress:focus-visible {
outline-color: var(--_progress);
outline-offset: 5px;
}
Niestandardowe style w różnych przeglądarkach
Dostosuj style, wybierając części elementu <progress>
widoczne dla każdej przeglądarki. Użycie elementu postępu to pojedynczy tag, ale składa się z kilku elementów podrzędnych, które są wyświetlane za pomocą pseudoselektorów CSS. Jeśli włączysz ustawienie,
Narzędzia deweloperskie w Chrome pokażą Ci te elementy:
- Kliknij stronę prawym przyciskiem myszy i wybierz Zbadaj element, aby wyświetlić Narzędzia deweloperskie.
- Kliknij ikonę koła zębatego (Ustawienia) w prawym górnym rogu okna Narzędzi deweloperskich.
- Pod nagłówkiem Elementy znajdź i zaznacz pole wyboru Pokaż cień klienta użytkownika.
Style Safari i Chromium
Przeglądarki oparte na WebKit, takie jak Safari i Chromium, ujawniają wartości ::-webkit-progress-bar
i ::-webkit-progress-value
, które umożliwiają używanie podzbioru CSS. Na razie ustaw background-color
, korzystając z utworzonych wcześniej właściwości niestandardowych, które dostosowują się do jasności i ciemności.
/* Safari/Chromium */
progress[value]::-webkit-progress-bar {
background-color: var(--_track);
}
progress[value]::-webkit-progress-value {
background-color: var(--_progress);
}
Style przeglądarki Firefox
Firefox wyświetla tylko pseudoselektor ::-moz-progress-bar
w elemencie <progress>
. Oznacza to również, że nie możemy zabarwić ścieżki bezpośrednio.
/* Firefox */
progress[value]::-moz-progress-bar {
background-color: var(--_progress);
}
Zwróć uwagę, że w przeglądarce Firefox jest ustawiony kolor ścieżki z accent-color
, a w przeglądarce iOS – jasnoniebieską. Tak samo wygląda to w trybie ciemnym: w przeglądarce Firefox występuje ciemna ścieżka, ale nie ma ona ustawionego przez nas koloru niestandardowego. Działa w przeglądarkach opartych na pakiecie Webkit.
Animacja
Podczas pracy z wbudowanymi pseudoselektorami w przeglądarce często ma ona ograniczony zestaw dozwolonych właściwości CSS.
Animowanie ścieżki w trakcie zapełniania się ścieżki
Dodanie przejścia do elementu inline-size
postępu działa w Chromium, ale nie w Safari. Firefox nie używa też właściwości przejścia w elemencie ::-moz-progress-bar
.
/* Chromium Only 😢 */
progress[value]::-webkit-progress-value {
background-color: var(--_progress);
transition: inline-size .25s ease-out;
}
Optymalizuję stan: :indeterminate
Teraz pokażę coś bardziej kreatywnego i animację. Zostanie utworzony pseudoelement dla Chromium i zastosowany gradient, który będzie animowany w obie strony we wszystkich trzech przeglądarkach.
Właściwości niestandardowe
Właściwości niestandardowe są przydatne w wielu przypadkach, ale jedną z moich ulubionych jest po prostu nazwanie wartości CSS, która wygląda inaczej. Poniższy fragment kodu linear-gradient
jest dość skomplikowany, ale z ładną nazwą. Można ją łatwo zrozumieć oraz zrozumieć jej cel i przypadki użycia.
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;
}
Właściwości niestandardowe także pomogą w utrzymaniu stanu DRY, ponieważ nie możemy grupować selektorów specyficznych dla danej przeglądarki.
Klatki kluczowe
Celem jest niekończąca się animacja, która pojawia się w obie strony. Początkowe i końcowe klatki kluczowe
będą ustawione w CSS. Potrzebna jest tylko 1 klatka kluczowa – środkowa klatka kluczowa w obszarze 50%
– do utworzenia animacji powracającej do miejsca, w którym została zainicjowana, i w kółko.
@keyframes progress-loading {
50% {
background-position: left;
}
}
Kierowanie na poszczególne przeglądarki
Nie każda przeglądarka umożliwia tworzenie pseudoelementów w samym elemencie <progress>
lub umożliwia animowanie paska postępu. Coraz więcej przeglądarek obsługuje
animację ścieżki, a nie pseudoelement, więc zamiast pseudoelementów używam pseudoelementów jako podstawy do animacji pasków.
Pseudoelement Chromium
Chromium zezwala na pseudoelement: ::after
używany z pozycją zasłonięcia elementu. Używane są nieokreślone właściwości niestandardowe, a animacje w przód i z powrotem działają bardzo dobrze.
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);
}
Pasek postępu w Safari
W przypadku Safari właściwości niestandardowe i animacja są stosowane do pseudoelementu paska postępu:
progress:indeterminate::-webkit-progress-bar {
background: var(--_indeterminate-track);
background-size: var(--_indeterminate-track-size);
background-position: right;
animation: var(--_indeterminate-track-animation);
}
Pasek postępu przeglądarki Firefox
W przeglądarce Firefox właściwości niestandardowe i animacja są też stosowane do pseudoelementu paska postępu:
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 odgrywa ważną rolę w elemencie <progress>
. Kontroluje on wartość wysyłaną do elementu i dba o to, aby dokument zawierał wystarczającą ilość informacji dla czytników ekranu.
const state = {
val: null
}
W wersji demonstracyjnej znajdują się przyciski do kontrolowania postępu: aktualizują się one state.val
, a następnie wywołują funkcję aktualizowania DOM.
document.querySelector('#complete').addEventListener('click', e => {
state.val = 1
setProgress()
})
setProgress()
To tu odbywa się administracja UI/UX. Zacznij od utworzenia funkcji setProgress()
. Nie są potrzebne żadne parametry, ponieważ ma on dostęp do obiektu state
, elementu postępu i strefy <main>
.
const setProgress = () => {
}
Ustawianie stanu wczytywania w strefie <main>
W zależności od tego, czy postęp został ukończony, powiązany element <main>
wymaga aktualizacji atrybutu aria-busy
:
const setProgress = () => {
zone.setAttribute('aria-busy', state.val < 1)
}
Usuń atrybuty, jeśli ilość wczytywania jest nieznana
Jeśli wartość jest nieznana lub nieskonfigurowana, null
w tym zastosowaniu usuń atrybuty value
i aria-valuenow
. Spowoduje to nieokreślenie stanu <progress>
.
const setProgress = () => {
zone.setAttribute('aria-busy', state.val < 1)
if (state.val === null) {
progress.removeAttribute('aria-valuenow')
progress.removeAttribute('value')
progress.focus()
return
}
}
Rozwiązywanie problemów z matematyką dziesiętną w JavaScript
Ponieważ zdecydowałem się zostać przy domyślnym ustawieniu postępu wynoszącym 1, funkcje demonstracyjnego zwiększania i zmniejszania korzystają z matematyki dziesiętnej. JavaScript i inne języki nie zawsze się w tym sprawdzają.
Oto funkcja roundDecimals()
, która pozwala usunąć nadmiar z wyniku matematycznego:
const roundDecimals = (val, places) =>
+(Math.round(val + "e+" + places) + "e-" + places)
Zaokrąglaj wartość, aby była widoczna i czytelna:
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 + "%"
}
Ustaw wartość czytników ekranu i stanu przeglądarki
Ta wartość jest używana w trzech miejscach w DOM:
- Atrybut
value
elementu<progress>
. - Atrybut
aria-valuenow
. - Wewnętrzna zawartość tekstowa funkcji
<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
}
Skupienie na postępach
Po zaktualizowaniu wartości widzący użytkownicy będą widzieć zmianę postępu, ale czytniki ekranu nie otrzymają jeszcze ogłoszenia o zmianie. Zaznacz element <progress>
, a przeglądarka poinformuje Cię o aktualizacji.
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()
}
Podsumowanie
Wiesz już, jak to zrobiłem, więc jak to zrobisz 🙂
Na pewno chciałbym wprowadzić kilka zmian, jeśli mam jeszcze jedną szansę. Wydaje mi się, że wystarczy wyczyścić bieżący komponent i spróbować utworzyć go bez ograniczeń dotyczących pseudoklasy w elemencie <progress>
. Warto sprawdzić to!
Stwórzmy różne metody i nauczmy się wszystkiego, jak rozwijać się w internecie.
Utwórz demonstrację i udostępnię mi linki na Twitterze, a dodam ją do sekcji remiksów w ramach społeczności poniżej.