こんにちは。KOUKIです。
本記事は、Udemyの「50 Projects In 50 Days – HTML, CSS & JavaScript」で学習したことを載せています。
実装するもの
今回は、DrinkWaterアプリを実装します。
demoは「こちら」で確認できます。
環境構築
簡単な環境構築をお願いします。
必要なファイルは、以下の通りです。
1 2 3 4 5 6 |
$ tree . ├── index.html ├── script.js └── style.css |
CSS版
ページ(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 25 26 27 28 29 30 31 32 33 34 35 36 37 |
<!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>Drink Water</title> </head> <body> <h1>Drink Water</h1> <h3>Goal: 2 Liters</h3> <div class="cup"> <div class="remained" id="remained"> <span id="liters"></span> <small>Remained</small> </div> <div class="percentage" id="percentage"></div> </div> <p class="text">Select how many glasses of water that you have drank</p> <div class="cups"> <div class="cup cup-small">250 ml</div> <div class="cup cup-small">250 ml</div> <div class="cup cup-small">250 ml</div> <div class="cup cup-small">250 ml</div> <div class="cup cup-small">250 ml</div> <div class="cup cup-small">250 ml</div> <div class="cup cup-small">250 ml</div> <div class="cup cup-small">250 ml</div> </div> <script src="script.js"></script> </body> </html> |
このHTMLをブラウザ上で表示すると以下のようになります。

スタイル(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 101 102 103 104 |
@import url('https://fonts.googleapis.com/css?family=Montserrat:400,600&display=swap'); :root { --border-color: #144fc6; --fill-color: #6ab3f8; } * { box-sizing: border-box; } body { background-color: #3494e4; color: #fff; font-family: 'Montserrat', sans-serif; display: flex; flex-direction: column; align-items: center; margin-bottom: 40px; } h1 { margin: 10px 0 0; } h3 { font-weight: 400; margin: 10px 0; } .cup { background-color: #fff; border: 4px solid var(--border-color); color: var(--border-color); border-radius: 0 0 40px 40px; height: 330px; width: 150px; margin: 30px 0; display: flex; flex-direction: column; overflow: hidden; } .cup.cup-small { height: 95px; width: 50px; border-radius: 0 0 15px 15px; background-color: rgba(255, 255, 255, 0.9); cursor: pointer; font-size: 14px; align-items: center; justify-content: center; text-align: center; margin: 5px; transition: 0.3s ease; } .cup.cup-small.full { background-color: var(--fill-color); color: #fff; } .cups { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; width: 280px; } .remained { display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; flex: 1; transition: 0.3s ease; } .remained span { font-size: 20px; font-weight: bold; } .remained small { font-size: 12px; } .percentage { background-color: var(--fill-color); display: flex; align-items: center; justify-content: center; font-weight: bold; font-size: 30px; height: 0; transition: 0.3s ease; } .text { text-align: center; margin: 0 0 5px; } |
ここまで実装すると以下のようになります。

補足: カスタムプロパティ
:root擬似クラス(文章のルート要素)に設定した値は、カスタムプロパティです。CSS文章全体で再利用できる値を定義できます。JavaScriptでいうところの変数に値します。
使用するときは、var(XXXX)のように指定して使用します。
1 2 3 4 5 6 7 8 9 |
// カスタムプロパティを宣言 :root { --fill-color: #6ab3f8; } // カスタムプロパティを使う .percentage { background-color: var(--fill-color); } |
JavaScriptの実装
準備ができたので、早速、DrinkWaterアプリを実装しましょう。
要素の取得
JavaScriptで制御すべき要素が4つあります。最初にこれらの要素を取得します。
1 2 3 4 5 |
// 要素を取得する const smallCups = document.querySelectorAll('.cup-small') const liters = document.getElementById('liters') const percentage = document.getElementById('percentage') const remained = document.getElementById('remained') |
ループ処理
取得したsmallCups要素をループで処理します。
1 2 3 4 5 |
// ループ処理 smallCups.forEach((cup, idx) => { console.log(idx) }) |
idxには、要素のインデックス番号が入ります。smallCups要素は8つあるので、「0~7」までの数値が取得できるということです。
クリックイベントを登録
次に、取得したsmallCupsの要素にクリックイベントを付与します。
1 2 3 4 |
smallCups.forEach((cup, idx) => { // クリックイベントを登録 cup.addEventListener('click', () => highlightCups(idx)) }) |
smallCups要素をハイライトする
続いて、smallCupsをハイライトする処理を実装します。ハイライト自体は、CSSの「full」クラスを各smallCupの要素に付与すればOKです。
1 2 3 4 5 6 7 8 9 10 11 12 |
// smallCupをハイライトする function highlightCups(idx) { smallCups.forEach((cup, idx2) => { if(idx2 <= idx) { // 指定したidxより前の全てのsmallCupにfullクラスをつける cup.classList.add('full') } else { // 指定したidxより後の全てのsmallCupからfullクラスを除去する cup.classList.remove('full') } }) } |
smallCup要素をクリックすると以下のようにハイライトされます。

ハイライトの解除
先ほどの実装だけだと既にハイライトされた要素を解除することができません。
そのため、以下の処理を追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
function highlightCups(idx) { // ハイライトを解除 if (idx===7 && smallCups[idx].classList.contains("full")) idx-- if(smallCups[idx].classList.contains('full') && !smallCups[idx].nextElementSibling.classList.contains('full')) { idx-- } smallCups.forEach((cup, idx2) => { if(idx2 <= idx) { ... } else { ... } }) } |
BigCup要素のアップデート関数
続いて、BigCup要素をアップデートする処理に移ります。とりあえず、関数だけ定義しておきます。
1 2 3 4 5 6 7 8 9 10 11 12 |
// smallCupをハイライトする function highlightCups(idx) { smallCups.forEach((cup, idx2) => { ... }) // BigCupをアップデート updateBigCup() } // BigCup要素のアップデート function updateBigCup(){} |
BigCup要素のハイライト処理
最後に、BugCup要素をハイライトする処理を実装して完了です。
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 |
// BigCup要素のアップデート function updateBigCup(){ // fullクラスがついた要素数を取得 const fullCups = document.querySelectorAll('.cup-small.full').length // smallCupsの要素数を取得(8) const totalCups = smallCups.length if(fullCups === 0) { // パーセンテージ要素を消す percentage.style.visibility = 'hidden' // パーセンテージ要素の高さを0 percentage.style.height = 0 } else { // パーセンテージ要素を表示 percentage.style.visibility = 'visible' // パーセンテージ要素の高さを設定 percentage.style.height = `${fullCups / totalCups * 330}px` // cssの.cupのheightを掛ける percentage.innerText = `${fullCups / totalCups * 100}%` } // 100%になった時の処理 if(fullCups === totalCups) { // remained文字を消す remained.style.visibility = 'hidden' remained.style.height = 0 } else { // remained文字を表示 remained.style.visibility = 'visible' liters.innerText = `${2 - (250 * fullCups / 1000)}L` } } |
おわりに
パーセンテージ毎にCSSで緩やかなアニメーションをつける所がいい感じですね-^^
他のアプリケーションにも応用できると思うので、今度作成してみます。
それでは、また!
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 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
// 要素を取得する const smallCups = document.querySelectorAll('.cup-small') const liters = document.getElementById('liters') const percentage = document.getElementById('percentage') const remained = document.getElementById('remained') // ループ処理 smallCups.forEach((cup, idx) => { // クリックイベントを登録 cup.addEventListener('click', () => highlightCups(idx)) }) // smallCupをハイライトする function highlightCups(idx) { // ハイライトを解除 if (idx===7 && smallCups[idx].classList.contains("full")) idx-- if(smallCups[idx].classList.contains('full') && !smallCups[idx].nextElementSibling.classList.contains('full')) { idx-- } smallCups.forEach((cup, idx2) => { if(idx2 <= idx) { // 指定したidxより前の全てのsmallCupにfullクラスをつける cup.classList.add('full') } else { // 指定したidxより後の全てのsmallCupからfullクラスを除去する cup.classList.remove('full') } }) // BigCupをアップデート updateBigCup() } // BigCup要素のアップデート function updateBigCup(){ // fullクラスがついた要素数を取得 const fullCups = document.querySelectorAll('.cup-small.full').length // smallCupsの要素数を取得(8) const totalCups = smallCups.length if(fullCups === 0) { // パーセンテージ要素を消す percentage.style.visibility = 'hidden' // パーセンテージ要素の高さを0 percentage.style.height = 0 } else { // パーセンテージ要素を表示 percentage.style.visibility = 'visible' // パーセンテージ要素の高さを設定 percentage.style.height = `${fullCups / totalCups * 330}px` // cssの.cupのheightを掛ける percentage.innerText = `${fullCups / totalCups * 100}%` } // 100%になった時の処理 if(fullCups === totalCups) { // remained文字を消す remained.style.visibility = 'hidden' remained.style.height = 0 } else { // remained文字を表示 remained.style.visibility = 'visible' liters.innerText = `${2 - (250 * fullCups / 1000)}L` } } |
コメントを残す
コメントを投稿するにはログインしてください。