今天要來實作的是進度條,我們可能在各種不同類型的網站上看過它的蹤影,像是註冊表單、結帳流程等,那今天就來試著來自己做吧ヽ(✿゚▽゚)ノ
一個作品是由一個個知識點堆疊而成,以下是本篇有使用到的知識點,應該是有全部列到啦
知識點 | 使用說明 |
---|---|
z-index | 控制順序 |
cursor | disabled的按鈕 |
transition | 進度線條的延展 |
2D transform | 預設進度線條的位子調整 |
variable | 變數存放顏色的變化 |
知識點 | 使用說明 |
---|---|
getElementById() / querySelectorAll() | 獲取HTML元素 |
addEventListener() | 監聽上一步和下一步按鈕 |
forEach() | 迭代每個步驟 |
.classList.remove() / add() | 新增或移除class="active" |
類似上一篇在做Expanding cards的配置,一樣是一個div容器內裝了子div,正在進行的那個步驟會加上class="active"去操控進度顯示
disabled
!因為最一開始是不會有上一步的選擇搭 <div class="container">
<div class="progress-container">
<div id="progress" class="progress"></div> //進度「線條」
<div class="circle active">1</div>
<div class="circle">2</div>
<div class="circle">3</div>
<div class="circle">4</div>
</div>
<!-- 上一步 -->
<button id="prev" type="button" class="btn" disabled>上一步</button>
<!-- 下一步 -->
<button id="next" type="button" class="btn">下一步</button>
</div>
* {
box-sizing: border-box;
}
body {
margin: 0;
padding: 0;
display: flex; /*讓內容在viewport的中間*/
justify-content: center;
align-items: center;
height: 100vh;
overflow: hidden;
}
若以上都設置好了,呈現會如下圖,接下來我們要把它變成橫向的排版
接下來的程式碼 ↓ ,主要是在針對進度條的容器去做美化、排版,值得注意的是我們在progress-container
內設了 max-width: 100%
和width:350px
,為甚麼要雙重設定呢??? 我們可以視它為一個防護措施,max-width
設定為百分比,是為了讓它不受限於父容器的寬度,而又有一個最少為350PX的寬度作為較安全的替代
外部和內部容器
/* 主容器 */
.container {
text-align: center;
font-size: 20px;
font-weight: bold;
}
/* 進度條容器 */
.progress-container {
display: flex;
justify-content: space-between;
position: relative; /*可以針對progress-containe內部物件的位子去做調整*/
margin-bottom: 30px;
max-width: 100%;
width: 350px;
}
變數設置
CSS3才出現的變數,主要是用來避免在不同的選擇器中重複定義相同的值,而變數名稱的開頭都是雙槓 --
,要使用的時候搭配var(--變數名稱)
就可以了。範例中我們把變數定義在:root
,讓它成為全域變數
--line-border-fill: #f33252; /*填滿狀態*/
--line-border-empty: #f4b9b9c6; /*未填滿狀態*/
--btn-hover: #ec5b73;
進度條容器的內部
以var(--變數名稱)
來取用背景顏色,transform: translateY(-50%)
是讓線條往Y軸的方向向上移動,百分比參考的是被定義transform的元件,而不是父元素,可以參考此連結
/* 預設進度「線條」 */
.progress-container::before {
content: "";
background-color: var(--line-border-empty);
position: absolute;
top: 50%;
left: 0;
width: 100%;
height: 3px;
z-index: -1;
transform: translateY(-50%); /*線條往上移動*/
}
/* 實際進度「線條」 */
div.progress {
background-color: var(--line-border-fill);
position: absolute;
top: 50%;
left: 0;
width: 0%; /*一開始不會有進度,所以設0%*/
height: 3px;
z-index: -1;
transform: translateY(-50%); /*線條往上移動*/
transition: width 0.5s linear;
}
/* 步驟1、2、3、4 */
div.circle {
color: white;
background-color: #f34764;
border: 3px solid var(--line-border-empty);
border-radius: 50%;
width: 30px;
height: 30px;
/* 讓數字置中對齊 */
display: flex;
justify-content: center;
align-items: center;
transition: border 0.5s linear;
}
/* 正在進行的步驟 */
div.circle.active {
border: 3px solid var(--line-border-fill);
}
「上一步」、「下一步」按鈕outline
是一個shorthand縮寫屬性,跟border很類似,不過一般是在表單輸入框被 focus 時,在外面那一圈變色的框線(Outline),這部分可以參考CSScoke大神寫的文章以及MDN
.btn {
color: white;
background-color: var(--line-border-fill);
border: 0;
border-radius: 10px;
cursor: pointer;
padding: 8px 30px;
font-size: 14px;
margin: 5px;
}
.btn:hover {
background-color: var(--btn-hover);
}
.btn:focus {
outline: 0; /*或none*/
}
.btn:disabled {
cursor: not-allow;
background-color: var(--line-border-empty);
}
建議可以寫一點就測一點,這樣要debug或是看某些設置的變化就就比較清楚! 如果有不知道甚麼就console.log()印出來看一下,有時候真的就豁然開朗
抓取HTML元素
let progress = document.getElementById("progress");
let prev = document.getElementById("prev");
let next = document.getElementById("next");
let circles = document.querySelectorAll(".circle");
功能的實現底家
因為進度條是跟著按鈕(next、prev)動態更新的,所以我們要加上addEventListener
去監聽事件的狀態,然後透過if判斷式
來決定是否可以再進行下一步/上一步,如果到最後一部,進度條就不會再往右方繼續延伸了;相反的,如果在第一步,進度條就不會再往左方繼續延伸了,所以我們要判斷目前是在第幾步驟,以便操控進度條的進展
let currentStep = 1; //目前步驟
// 下一步
next.addEventListener("click", () => {
currentStep++;
// console.log(currentStep);
if (currentStep > circles.length) {
currentStep = circles.length;
}
// console.log(currentStep);
update();
});
// 上一步
prev.addEventListener("click", () => {
currentStep--;
// console.log(currentStep);
if (currentStep < 1) {
currentStep = 1;
}
console.log(currentStep);
update();
});
DOM的更新
function update() {
// 圓圈
circles.forEach((circleItem, index) => {
if (index < currentStep) {
circleItem.classList.add("active");
} else {
circleItem.classList.remove("active");
}
});
當前進度的線條
這裡有一個很重要的點,一定要搞懂!
就是progress.style.width =(actives.length - 1) / (circles.length - 1)) * 100 + "%"
let actives = document.querySelectorAll(".active");
console.log((actives.length / circles.length) * 100);
// progress.style.width = (actives.length / circles.length) * 100 + "%";
progress.style.width =
((actives.length - 1) / (circles.length - 1)) * 100 + "%";
if (currentStep === 1) {
prev.disabled = true;
} else if (currentStep === 4) {
next.disabled = true;
} else {
prev.disabled = false;
next.disabled = false;
}
}
我整理了自己當時在做的時候出現的疑問,或許也有可能是其他人的疑問
Q1 為什麼要用除法?
Q2 為什麼要-1?
Q3 為甚麼要加上%在最後面?
A1 把它轉換成分數的概念
A2 我們來比較看看有-1和沒-1的差別
沒-1的版本,進度分成了1/2,換句話說,就是你每按下一步,進度就會前進1/2(約50%),這樣是不對的
有-1的版本,進度條就被分成1/3,換句話說,你每按下一步,進度就會前進1/3(約33%),這才是正確的actives.length - 1 = 2 - 1 = 1
circles.length - 1 = 4 -1 = 3
A3 加上百分比是因為progress的width
我們預設單位是%,所以才要用%
附上進度條的Codepen連結https://codepen.io/hangineer/pen/mdLVREN
可以順一下進度條的邏輯是怎麼運作的,會用到簡單的數學概念,並且一段一段來,如果還是有疑問,可以底下留言,我會盡我所能地回答你。
這篇的篇幅較長,還在想要用甚麼樣的方式呈現會比較好ಥ_ಥ 如果邦友們有任何想法可以留言讓我知道~ 所學不精,若有解說不夠詳盡或是錯誤歡迎指教,感激不盡!那明天見囉
50 Projects In 50 Days - HTML, CSS & JavaScript
CSS視覺辭典 Greg Sidelnikov者