這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 步驟條是一種用於引導用戶按照特定流程完成任務的導航條,在各種分步表單交互場景中廣泛應用。先來看一下幾個主流前端 UI 框架中步驟條組件的樣子: ElementPlus AntDesign OpenTiny iView 我們可以發現,步驟條 ...
這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助
步驟條是一種用於引導用戶按照特定流程完成任務的導航條,在各種分步表單交互場景中廣泛應用。先來看一下幾個主流前端 UI 框架中步驟條組件的樣子:
我們可以發現,步驟條通常由編號、名稱和引導線三個基本要素組成。本文中要實現的是一個簡單的步驟條,包含上述三個基本要素,下麵是最終的效果圖:
接下來將詳細介紹實現過程。
確定結構
對於步驟條這種呈現順序的列表結構,在 HTML 標簽選擇上,使用 ul
或 ol
標簽可以讓語義更加清晰,這裡我們使用了 ol
標簽,HTML 代碼如下:
<ol class="steps"> <li>註冊</li> <li>域認證</li> <li>身份校驗</li> <li>風險等級評估</li> <li>開通賬號</li> </ol>
由於步驟項需要水平排列,因此在 CSS 中用了 flex 佈局,代碼如下所示:
.steps { display: flex; justify-content: space-between; /* 按水平均勻分佈,行首行尾兩端靠齊 */ margin: 0; }
現在,我們的“步驟條”已初步有那麼點兒意思了,讓我們繼續完善細節。
生成步驟編號
步驟編號可利用 CSS 的原生能力來自動生成,在上一步中,ol
標簽生成的編號並不好看,此處我們用 ::before
偽元素和 CSS 計數器來實現帶圈編號的樣式,讓步驟看起來更加清晰明瞭。
感興趣的小伙伴可移步《CSS實現有序列表編號方法知多少》一文查看相關知識點
.steps { display: flex; justify-content: space-between; padding: 0; margin: 0; counter-reset: order; /* 定義CSS計數器 */ list-style: none; } .steps > li {counter-increment: order;} .steps > li::before { content: counter(order); /* 編號 */ display: inline-block; width: 1.4em; line-height: 1.4em; margin-right: .5em; vertical-align: middle; text-align: center; border-radius: 50%; border: 1px solid; }
實現效果如下圖所示:
實現引導線
我們已經有了步驟編號和名稱,接下來需要實現步驟引導線。
引導線將各個步驟項連接為一體,使流程在視覺上具有指向性,它是個裝飾性元素,所以不應該出現在 HTML中。由於前面的 ::before
已經用於步驟編號,所以我們選擇使用 ::after
來實現引導線。
.steps > li::after { content: ''; display: inline-block; width: 60px; vertical-align: middle; /* 讓引導線和文本垂直居中 */ border-bottom: 1px solid #ccc; }效果如下圖所示:
最後一個步驟項是不需要引導線的,所以我們改用 :not
偽類選擇器把它過濾掉:
.steps > li:not(:last-child)::after { ... }現在我們面臨一個難題:怎樣確定引導線的寬度呢?使用固定寬度顯然行不通,因為這會有很大的局限性。理想的解決方案是引導線寬度能夠自動適應,占據除編號和名稱文本以外的剩餘空間。這種寬度自適應的場景,我們會很自然想到用 flex 佈局來解決:只需將每個步驟項
li
標簽的佈局屬性改為 inline-flex
盒子即可。
.steps > li { flex: auto; /* 彈性寬度(根據其內容來調整) */ display: inline-flex; /* 內聯塊級彈性伸縮盒子 */ align-items: center; counter-increment: order; } .steps > li:not(:last-child)::after { content: ''; flex: 1; /* 占滿 li 中的剩餘寬度 */ margin: 0 1em; border-bottom: 1px solid #ccc; }
現在的佈局效果已經非常接近目標了:
如果我們看得仔細一些,就會發現在最後一個步驟項的右邊出現了一段空白,實際中我們希望它能夠和右邊對齊。
li
標簽的 flex: auto
這個 CSS 屬性有關,該屬性會根據當前容器的可用寬度來分配父容器寬度,當分配後還有剩餘寬度時,前幾個步驟項會有 CSS 屬性為 flex: 1
的引導線來填補剩餘寬度,但最後一個步驟項沒有引導線,因此會出現空白。在瞭解根因後,我們只需要調整最後一個步驟項即可解決這個問題:
.steps > li:last-child { flex: none; }
同時我們也意識到,當步驟項容器寬度不夠時,作為 flex
子元素的圓形編號可能會被擠壓變形:
解決方案也很簡單,禁止 flex 子元素收縮:
.steps > li::before { ... flex-shrink: 0; /* 佈局寬度不夠時禁止收縮 */ }
步驟條狀態
在調教好佈局結構之後,我們來為步驟條增加狀態。通常情況下,步驟條狀態包括“已完成”、“進行中”和“未開始”三種,對應的裝飾樣式如下表所示:
狀態 | 步驟編號 | 步驟名稱 | 步驟引導線 |
---|---|---|---|
已完成 | 無背景色,邊框和文本高亮色 | 文本高亮色 | 高亮色 |
進行中 | 背景和邊框高亮色,文本反色 | 文本高亮色 | 普通色 |
未開始 | 無背景色,邊框和文本普通色 | 文本普通色 | 普通色 |
對此我們定義普通色和高亮色這2個顏色變數,以方便代碼維護和擴展。
.steps { --normal-color: #666; /* 普通色 */ --active-color: #06e; /* 高亮色 */ ... }
然後將所有步驟項預設以普通色呈現:
.steps > li { ... color: var(--normal-color); }
引導線的顏色則預設自動繼承字體顏色,同時為了避免引導線喧賓奪主,我們給它加了個透明度控制下顏色深度:
.steps > li:not(:last-child)::after { ... border-bottom: 1px solid; /* 不指定顏色,則自動繼承自身color或父級color */ opacity: .6; }
接下來是“已完成”和“進行中”的樣式定義,需要註意“進行中”後面的引導線不能高亮。
.steps > .done, .steps > .active { color: var(--active-color); } .steps > .active::before { color: #fff; background: var(--active-color); border-color: var(--active-color); } .steps > .active::after { color: var(--normal-color); /* “進行中”後面的引導線按普通色顯示 */ }
然後在 HTML 中調用對應的樣式鉤子:
<ol class="steps"> <li class="done">註冊</li> <li class="done">域認證</li> <li class="done">身份校驗</li> <li class="active">風險等級評估</li> <li>開通賬號</li> </ol>
實現效果如下圖所示:
最終方案
就顯示效果而言,現在可以收工了,但對於將優雅奉為圭臬的程式猿來說,這個步驟條還差點意思——用 done
和 active
樣式鉤子來分別標記“已完成”和“進行中”的狀態——這並不優雅。
<ol class="steps"> <li class="done">註冊</li> <!-- 已完成 --> <li class="done">域認證</li> <!-- 已完成 --> <li class="done">身份校驗</li> <!-- 已完成 --> <li class="active">風險等級評估</li> <!-- 進行中 --> <li>開通賬號</li> </ol>如果回想一下我們在前端 UI 框架中使用的步驟條,就會發現它只要關心當前進展到了哪一步,而“已完成”和“未完成”都是被組件內部自行處理的。那麼,對於我們現在所做的步驟條,是否可以僅標記“進行中”,也就是只使用
active
樣式鉤子就可以了呢?就像下麵這樣:
<ol class="steps"> <li>註冊</li> <li>域認證</li> <li>身份校驗</li> <li class="active">風險等級評估</li> <!-- 進行中 --> <li>開通賬號</li> </ol>
對於這樣的 HTML 結構,active
這個鉤子可繼續沿用之前的 CSS 代碼,實現當前步驟項的高亮效果,然後可以根據 active
這個類名匹配它前面的兄弟步驟項,實現與 done
這個類一樣的效果。不過我們很快就會被現實打臉:CSS 中根本沒有“前兄弟選擇器”這種東西,因此無法根據 active
向前匹配。
於是我們需要調整思路,逆向思考:既然無法匹配 active
前面的元素,那為什麼不匹配其後面的元素呢?畢竟 CSS 中是有兄弟選擇器的呀,至於 active
前面的元素,或許我們可以通過其父級來控制樣式?
現在思路清晰了許多。我們先把所有步驟項都預設設置為“已完成”狀態的高亮樣式:
.steps > li { ... color: var(--active-color); /* 改為“已完成”,之前的值是 var(--normal-color) */ }此時步驟條變成了這樣:
然後加上 active
的樣式,假設當前是第4步,則效果如下:
active
後面的步驟項改成“未開始”的樣式,利用兄弟選擇器輕鬆搞定:
.steps > .active ~ li { color: var(--normal-color); }
最後再來測試下整體效果:
最終完整的 CSS 代碼如下:
.steps { --normal-color: #666; --active-color: #06e; display: flex; justify-content: space-between; padding: 0; margin: 0; counter-reset: order; } /* 步驟項 */ .steps > li { flex: auto; display: inline-flex; align-items: center; counter-increment: order; color: var(--active-color); } .steps > li:last-child {flex: none;} /* 步驟編號(帶圈數字) */ .steps > li::before { content: counter(order); flex-shrink: 0; width: 1.4em; line-height: 1.4em; margin-right: .5em; text-align: center; border-radius: 50%; border: 1px solid; } /* 步驟項引導線 */ .steps > li:not(:last-child)::after { content: ''; flex: 1; margin: 0 1em; border-bottom: 1px solid; opacity: .6; } /* 步驟狀態 */ .steps > .active {color: var(--active-color);} .steps > .active::before { color: #fff; background: var(--active-color); border-color: var(--active-color); } .steps > .active::after, .steps > .active ~ li {color: var(--normal-color);}
本文附件中提供完整代碼的 demo,感興趣的小伙伴可聯繫我們獲取,可在現有基礎上定製、擴展。下麵是拋磚引玉:
知識點總結
- flex 容器的
justify-content: space-between;
可令子元素按顯示方向均勻分佈,兩端分散對齊,實在是居家旅行之必備神器; inline-flex
的盒子既能像 flex 容器那樣輕鬆拿捏其子元素的佈局,又能像行內塊元素一樣平易近人;- CSS 計數器洗剪吹一條龍:
counter-reset
、counter-increment
、counter(xxx)
; flex: <number>
對於寬度(或高度)能占盡占,該是我的就是我的,能剩一點算我輸;flex: auto
從自身實際情況出發應占盡占,大家共同富裕不香嗎;flex-shrink
用來設置 flex 元素的可壓榨基準,與它對應的是flex-basis
,用來設置可膨脹基準;- IE 都亡了,CSS 變數,放心用起來吧;
- 強大的 CSS 偽類選擇器,可以讓代碼更精簡,還可以打出組合拳:
li:not(:last-child)::after
; - 平平無奇關鍵時刻又能打能抗的兄弟選擇器:
.active ~ li
。