建立載入列元件

基本總覽:如何使用 <progress> 元素建構可自動調整顏色且可存取的載入列。

在這篇文章中,我想分享一些想法,包括如何打造自動調整色彩及 含有 <progress> 元素的可存取載入列。試試 示範觀看 資料來源

Chrome 降低了淺色和黑暗、不定、增加和完成度等設定。

如果您喜歡看影片,請參考這篇文章的 YouTube 版本:

總覽

<progress>敬上 元素提供視覺和音訊意見回饋,讓使用者知道應用程式已完成。這個 視覺回饋適用於各種情境,例如填寫表單、 或顯示下載或上傳資訊 進度金額不明,但作業仍在執行中。

這項 GUI 挑戰 現有的 HTML <progress> 元素,節省一些無障礙功能的工作。 色彩和版面配置,突破了內建元素自訂的極限, 翻新元件,讓元件與設計系統相輔相成。

在每個瀏覽器中 
    自動調整圖示從上到下簡介: 
    Safari、Firefox、Chrome。
在 Firefox、Safari、iOS Safari、 Chrome 和 Android Chrome 的淺色和深色配置。

標記

我選擇將 <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>

透過 WebAIM 提供的下列隨附 CSS:

.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> 版面配置

進度元素的寬度維持不變,方便縮小並放大 達成設計所需的空間內建的樣式會由 正在將 appearanceborder 設為 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;
}

_radius1e3px 值使用科學數字 表示方式 大數值,因此 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);
}
敬上

載入列位於 100% 且尾端顯示勾號的螢幕截圖。

顏色

瀏覽器會為進度元素顯示專屬顏色,並且會根據 只需要一項 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 開發人員工具 如果啟用設定,畫面上就會顯示這些元素:

  1. 在頁面上按一下滑鼠右鍵,然後選取「檢查元素」開啟開發人員工具。
  2. 按一下開發人員工具視窗右上角的「設定」齒輪。
  3. 在「元素」Elements標題下方,找到並啟用「顯示使用者代理程式陰影」 DOM」核取方塊。

螢幕截圖:在開發人員工具中的哪個位置啟用使用者代理程式 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 的螢幕截圖,以及進度元素部分的位置。

Safari、iOS Safari、 
  Firefox、Chrome 和 Android 版 Chrome 都會顯示載入列。

請注意,當 iOS Safari 顯示 Firefox 時,Firefox 的軌跡顏色是由 accent-color 設定。 都有淺藍色軌道深色模式也一樣:Firefox 具備深色軌跡, 而不是我們設定的自訂顏色,而且這個顏色可以在採用 Webkit 技術的瀏覽器中使用。

動畫

使用瀏覽器內建的虛擬選取器時,通常只會遇到 一系列允許的 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()

這個函式會執行 UI/使用者體驗自動化調度管理。請先建立 setProgress() 函式。不需要參數,因為 state 物件、進度元素和 <main> 可用區。

const setProgress = () => {
  
}

設定 <main> 可用區的載入狀態

視進度是否完成而定,相關的 <main> 元素需要更新 aria-busy 屬性:

const setProgress = () => {
  zone.setAttribute('aria-busy', state.val < 1)
}

如果載入量不明,則清除屬性

若是未知或未設定的值,null在此用法中,請移除 valuearia-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 中的三個位置:

  1. <progress> 元素的 value 屬性。
  2. aria-valuenow 屬性。
  3. <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()
}

Mac OS Voice Over 應用程式的螢幕截圖 
  讀取載入列的進度

結論

現在你知道我怎麼了,這樣會如何 🙂?

假如再有一次機會,我一定會進行一些變更。我認為還有空間清除目前的元件,並且有空間嘗試在沒有 <progress> 元素虛擬類別樣式限制的情況下,建構一個元件。值得一探究竟!

讓我們來體驗多元的方法,瞭解透過網路建立內容的所有方式。

建立示範、將 Twitter 推文連結,我們就會為您新增 前往下方的社群重混專區!

社群重混作品