こんにちは。KOUKIです。
本記事は、Udemyの「50 Projects In 50 Days – HTML, CSS & JavaScript」で学習したことを載せています。
<目次>
実装するもの
今回は、プログレスバーの状態変更をJavaScriptで操作しようと思います。
demoは、「こちら」で確認できます。
環境構築
最初に、簡単な環境構築をお願いします。
必要なファイルは、以下の通りです。
1 2 3 4 5 6 |
$ tree . ├── index.html ├── script.js └── style.css |
CSS版
開発
ページの作成
最初にページの作成をしましょう。index.htmlに次のコードを記述してください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link rel="stylesheet" href="style.css" /> <title>Progress Steps</title> </head> <body> <div class="container"> <div class="progress-container"> <div class="progress" id="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 class="btn" id="prev" disabled>Prev</button> <button class="btn" id="next">Next</button> </div> <script src="script.js"></script> </body> </html> |
このHTMLをブラウザ上で表示すると以下のようになります。

スタイルの追加
次にスタイルをstyle.cssに記述してきましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
@import url("https://fonts.googleapis.com/css?family=Muli&display=swap"); :root { --line-border-fill: #3498db; --line-border-empty: #e0e0e0; } * { box-sizing: border-box; } body { background-color: #f6f7fb; font-family: "Muli", sans-serif; display: flex; align-items: center; justify-content: center; height: 100vh; overflow: hidden; margin: 0; } .container { text-align: center; } .progress-container { display: flex; justify-content: space-between; position: relative; margin-bottom: 30px; max-width: 100%; width: 350px; } .progress-container::before { content: ""; background-color: var(--line-border-empty); position: absolute; top: 50%; left: 0; transform: translateY(-50%); height: 4px; width: 100%; z-index: -1; } .progress { background-color: var(--line-border-fill); position: absolute; top: 50%; left: 0; transform: translateY(-50%); height: 4px; width: 0%; z-index: -1; transition: 0.4s ease; } .circle { background-color: #fff; color: #999; border-radius: 50%; height: 30px; width: 30px; display: flex; align-items: center; justify-content: center; border: 3px solid var(--line-border-empty); transition: 0.4s ease; } .circle.active { border-color: var(--line-border-fill); } .btn { background-color: var(--line-border-fill); color: #fff; border: 0; border-radius: 6px; cursor: pointer; font-family: inherit; padding: 8px 30px; margin: 5px; font-size: 14px; } .btn:active { transform: scale(0.98); } .btn:focus { outline: 0; } .btn:disabled { background-color: var(--line-border-empty); cursor: not-allowed; } |
ここまで実装すると以下のようになります。

CSSは、本当に難しいですね^^;ここまでできるんだっていう感じです。
JavaScriptの実装
さて、いよいよJavaScriptを実装しましょう。scripts.jsに書き込んでいきます。
要素を取得する
JavaScriptで制御すべき要素が4つのあります。最初にこれらの要素を取得しましょう。
1 2 3 4 5 6 7 8 9 |
// 要素を取得 // プログレスばー const progress = document.getElementById('progress') // prevボタン const prev = document.getElementById('prev') // nextボタン const next = document.getElementById('next') // サークル(円) const circles = document.querySelectorAll('.circle') |
クリックイベントの登録
次にprev/nextボタンのクリックイベントの登録を行います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
// 現在アクティブになっているサークルを示す let currentActive = 1 // nextボタンのクリックイベント next.addEventListener('click', () => { currentActive++ // クリックした数がサークル数を超えないように制御する(最大4) if(currentActive > circles.length) { currentActive = circles.length } }) // prevボタンのクリックイベント prev.addEventListener('click', () => { currentActive-- // activeがマイナスにならないように制御する if(currentActive < 1) { currentActive = 1 } }) |
これでボタンを押下した時には、イベントが発生します。
プログレスの状態を更新する
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
next.addEventListener('click', () => { ... // update update() }) prev.addEventListener('click', () => { ... // update update() }) // プログレスの状態を更新 function update() { // サークルの要素数文ループする circles.forEach((circle, idx) => { // indexが現在のアクティブより小さい場合 if(idx < currentActive) { // activeクラスを付与する circle.classList.add('active') } else { // そうでない場合は、activeクラスを除去する circle.classList.remove('active') } }) } |
activeクラスを付与することで、サークルの周りが青く縁取られるようになります。
試しにnextボタンを押下してみましょう。

idxは、要素数(index)を表していて、この要素が現在Activeになっているべき数(currentActive)より小さい場合はその要素にactiveクラスをつけます。
プログレスバーのスタイル制御
プログレスバーのスタイルは、以下のように定義していました。
1 2 3 4 5 6 7 8 9 10 11 |
.progress { background-color: var(--line-border-fill); position: absolute; top: 50%; left: 0; transform: translateY(-50%); height: 4px; width: 0%; z-index: -1; transition: 0.4s ease; } |
現在は、width:0%になっていますが、この値を変更するとプログレスバーのスタイルが変化します。例えば、width: 100%にしてみましょう。

プログレスバーが青く縁取られましたね。JavaScriptでこの値を操作して状態を変化させてみましょう。
update関数に以下のプログラムを追加します。
1 2 3 4 5 6 7 8 9 10 11 |
function update() { ... // プログレスバーの線の制御 // activeクラスが付与されている要素を取得する const actives = document.querySelectorAll('.active') // active数 / サークルの数 * 100(%) = 進捗状態 progress.style.width = (actives.length -1) / (circles.length -1) * 100 + '%' } |
「progress.style.width」のように要素のスタイルを直接指定すると値を変更できます。便利ですよね。
また、このプログラムだとサークルとサークルの間をだいたい33%に指定するといい感じになるので、それぞれの要素を-1しています(1/3*100=33.333…)。
ボタンの活性制御
最後にボタンの活性制御を実装して、終了しましょう。
現状、prevボタンは非活性状態になっていますが、これは要素にdisabledを指定しているからです。
1 |
<button class="btn" id="prev" disabled>Prev</button> |
先ほど取得した要素から状態を変更できることを学んだように(progress.style.width)、ボタンの活性状態も「prev.disabled = true(or false)」と指定して操作します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
unction update() { ... // ボタンの活性制御 if(currentActive === 1) { prev.disabled = true } else if(currentActive === circles.length) { next.disabled = true } else { prev.disabled = false next.disabled = false } } |
このプログラムは、以下の条件分岐を表しています。
- active数が1(最小)の場合は、prevボタンを非活性
- active数が4(最大)の場合は、nextボタンを非活性
- active数が最大・最小以外は、prev/nextボタンを活性
試しに、nextボタンを押下してみるとprevボタンが活性状態になることがわかります。

おわりに
JavaScript自体はそれほど難しくないはずです。一番難しいのは、CSSですかね^^;
もっと簡単にプログレスバーを実装したいという方は、BootStrapの「Progress – Bootstrap」の導入を検討してみてはいかがでしょうか?かなり便利ですよ。
それでは、また!
JavaScriptまとめ
JavaScript ソースコード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
const progress = document.getElementById('progress') const prev = document.getElementById('prev') const next = document.getElementById('next') const circles = document.querySelectorAll('.circle') let currentActive = 1 next.addEventListener('click', () => { currentActive++ if(currentActive > circles.length) { currentActive = circles.length } update() }) prev.addEventListener('click', () => { currentActive-- if(currentActive < 1) { currentActive = 1 } update() }) function update() { circles.forEach((circle, idx) => { if(idx < currentActive) { circle.classList.add('active') } else { circle.classList.remove('active') } }) const actives = document.querySelectorAll('.active') progress.style.width = (actives.length -1) / (circles.length -1) * 100 + '%' if(currentActive === 1) { prev.disabled = true } else if(currentActive === circles.length) { next.disabled = true } else { prev.disabled = false next.disabled = false } } |
コメントを残す
コメントを投稿するにはログインしてください。