はじめに
こんにちは、株式会社Techoesの C (入社から半年目の初心者エンジニア)と申します!
現在、HTML・CSS・JavaScriptを学習しています。
前回の記事では、Codexを使ってゲームを作った経験を紹介しました。今回は先輩からの提案をきっかけに、自分でコードを書きながら、簡単なWebアプリを作ってみました。
最初は電卓を考えていましたが、日常でも使えるものにしたいと思い、「割り勘計算アプリ」を作ることにしました。
この記事では、アプリを作る中でつまずいたことや、学んだことを紹介します。
2. 今回作る割り勘計算アプリ
お会計の金額と人数を入力して「計算」ボタンを押すと、1人あたりの金額が表示されるアプリです。集める金額の合計と、お会計との差額も表示します。
1人あたりの金額は、1円未満を切り上げています。例えば、3,500円を3人で割ると1人あたり1,167円になり、合計で1円多く集まります。
入力欄が空の場合や、0・負数・小数を入力した場合は、エラーメッセージが表示されます。「クリア」ボタンを押すと、入力内容と計算結果をリセットできます。
HTML・CSS・JavaScriptを使って作りました。スマートフォンでも使えるように、画面幅に合わせて余白や文字サイズを調整しています。
3. HTMLで入力欄とボタンを作る
まず、お会計金額と人数の入力欄を作りました。その下に「計算」「クリア」のボタンと、結果を表示するエリアを用意しました。
<div class="app">
<h1>割り勘計算アプリ</h1>
<p>お会計の金額と人数から、1人分の金額を計算します。</p>
<div class="field">
<!-- labelは入力欄の説明。forを入力欄のidに合わせる -->
<label for="money">お会計金額(円):</label>
<!-- 数値の入力欄を作成し、idで要素を識別する -->
<input type="number" id="money">
</div>
<div class="field">
<label for="people">人数(人):</label>
<input type="number" id="people">
</div>
<div class="actions">
<button class="calculate-btn" onclick="calculate();">計算</button>
<button class="clear-btn" onclick="clearInput();">クリア</button>
</div>
<div id="result">まだ計算していません</div>
<p class="note">1人分の金額は、1円未満を切り上げます。</p>
</div>
入力欄にはtype="number"を指定し、金額にはid="money"、人数にはid="people"を付けました。後からJavaScriptで、それぞれの入力欄を取得するために使います。また、labelのforを入力欄のIDと合わせて、説明文と入力欄を結び付けています。
ボタンにはonclickを設定し、「計算」を押すとcalculate()、「クリア」を押すとclearInput()が実行されるようにしました。それぞれの関数の中身は、次の章から説明します。
結果を表示するエリアにはid="result"を付けました。最初は「まだ計算していません」と表示し、計算後にJavaScriptで結果に書き換えます。
class="field"とclass="actions"は、後からCSSで入力欄やボタンの配置を整えるために付けています。
4. JavaScriptで入力値を取得して計算する
計算ボタンにはonclick="calculate();"を設定しています。ボタンを押すとcalculate()関数が実行され、入力値の取得から計算結果の表示までを行います。
const moneyInput = document.querySelector("#money");
const peopleInput = document.querySelector("#people");
const money = Number(moneyInput.value);
const people = Number(peopleInput.value);
document.querySelector()を使って、金額と人数の入力欄を取得します。#moneyと#peopleは、HTMLで設定したIDに対応しています。
次に、.valueで入力内容を読み取ります。入力欄にtype="number"を指定していても、取得する値は文字列なので、Number()で数値に変換します。
最初は、querySelector()で入力欄の内容を直接取得できると思っていました。実際に使ってみて、取得するのは入力欄の要素そのもので、入力された値を読むには.valueが必要だと分かりました。
これらの変数は宣言後に再代入しないため、constを使いました。
const amountPerPerson = Math.ceil(money / people);
const collectedTotal = amountPerPerson * people;
const extra = collectedTotal - money;
result.innerHTML =
"1人あたりの金額<br>" +
"<span class='amount'>" + amountPerPerson + " 円</span><br>" +
"集める金額の合計:" + collectedTotal + " 円<br>" +
"お会計との差額:" + extra + " 円";
金額と人数を取得した後、お会計金額を人数で割って、1人あたりの金額を計算します。今回新しく学んだMath.ceil()を使い、1円未満を切り上げました。詳しい使い方は第6章で紹介します。
続いて、1人あたりの金額に人数を掛けて、集める金額の合計を求めます。そこから元のお会計金額を引いて、差額を計算します。
結果表示エリアは、document.querySelector("#result")で取得しています。最後に、result.innerHTMLで結果表示エリアの内容を更新します。<br>で改行し、1人あたりの金額を<span class='amount'>で囲んで、CSSで大きく表示できるようにしました。
5. 入力チェックとクリア機能を追加する
入力チェック
計算処理だけでは、未入力の場合や、金額・人数に0を入力した場合も計算が実行されます。そこで、計算前に入力をチェックし、金額と人数はどちらも0より大きい整数に限定しました。
if (money <= 0) {
result.className = "error";
result.innerHTML = "金額は0より大きい数値を入力してください";
return;
}
if (!Number.isInteger(money)) {
result.className = "error";
result.innerHTML = "金額は整数(円単位)で入力してください";
return;
}
入力欄が空の場合、Number("")は0になります。そのため、<= 0で未入力・0・負数をチェックできます。小数はNumber.isInteger()で判定します。先頭の!で判定結果を反転させ、整数ではない場合にエラーとして扱います。
入力に問題があれば、result.innerHTMLでメッセージを表示し、returnで関数の処理を終了します。これにより、その後の計算は実行されません。
また、result.className = "error"でエラー用のスタイルを適用し、メッセージを赤く表示します。計算を実行するたびに、最初にresult.className = ""でエラー用のスタイルを解除し、入力を修正した後は通常の表示に戻るようにしました。
クリア機能
function clearInput() {
const moneyInput = document.querySelector("#money");
const peopleInput = document.querySelector("#people");
const result = document.querySelector("#result");
moneyInput.value = "";
peopleInput.value = "";
result.className = "";
result.innerHTML = "まだ計算していません";
}
クリアボタンを押すと、2つの入力欄の.valueを空文字列にして、入力内容を消します。同時にエラー用のスタイルを解除し、結果表示エリアを「まだ計算していません」に戻して、もう一度入力できるようにしました。
6. 端数の切り上げと差額の計算
お会計金額が人数で割り切れるとは限りません。例えば、3,500円を3人で割ると、1人あたりの金額は約1166.666…円になります。そのため、端数をどう扱うかを考える必要がありました。
今回新しく学んだMath.ceil()は、数値を整数に切り上げるメソッドです。例えば、Math.ceil(1166.666…)の結果は1167になります。もともと整数の場合は、そのままの値になります。
const amountPerPerson = Math.ceil(money / people);
const collectedTotal = amountPerPerson * people;
const extra = collectedTotal - money;
まず、お会計金額を人数で割り、Math.ceil()で切り上げて、1人あたりの金額を求めます。次に、その金額に人数を掛けて集金額の合計を計算し、元のお会計金額を引いて差額を求めます。
今回の例では、次のようになります。
| 項目 | 金額 |
|---|---|
| お会計金額 | 3,500円 |
| 1人あたりの金額 | 1,167円 |
| 3人分の集金額 | 3,501円 |
| 差額 | 1円 |
切り上げることで集金額が不足するのを防げますが、お会計金額より少し多く集める場合があります。そのため、いくら多く集めることになるのか分かるように、差額も画面に表示しました。
7. CSSで画面を整える
機能ができた後、CSSで入力欄やボタンの余白を整えました。計算ボタンとクリアボタンはFlexboxで横並びにし、計算ボタンにflex: 1を指定して、残りのスペースを埋めるようにしました。
結果表示エリアは薄い緑色にし、1人あたりの金額を大きく太字で表示しました。入力に問題がある場合は、errorクラスで赤い表示に切り替えています。
スマートフォン向けには、@media (max-width: 500px)で余白と見出しの文字サイズを小さくしました。Chromeのデバイスモードを使い、狭い画面でも入力欄・ボタン・結果が収まることを確認しました。
@media (max-width: 500px) {
body {
padding: 12px;
}
.app {
margin: 12px auto;
padding: 20px;
}
h1 {
font-size: 24px;
}
}
8. 動作確認と、つまずいた点
機能ができた後、正常な入力に加えて、未入力・0・負数・小数の場合も確認しました。制作中には、ボタンを押しても画面が変わらないことがありました。
関数名が原因でボタンが動かなかった
最初は計算用の関数をclick()と名付け、ボタンにonclick="click();"を設定していました。名前を合わせれば動くと思っていましたが、ボタンを押しても結果表示エリアが更新されませんでした。
以下は、当時の問題を再現したコードです。
<button class="calculate-btn" onclick="click();">計算</button>
<button class="clear-btn" onclick="clear();">クリア</button>
<script>
function click() {
// 計算処理(省略)
}
function clear() {
// クリア処理(省略)
}
</script>
原因を調べると、ボタンにはもともとclick()というメソッドがあり、この書き方では自分で作った関数ではなく、そちらが呼び出されていました。
関数名をcalculate()に変更し、ボタンのonclickも合わせて修正すると、正常に動くようになりました。
<button class="calculate-btn" onclick="calculate();">計算</button>
<button class="clear-btn" onclick="clearInput();">クリア</button>
<script>
function calculate() {
// 計算処理(省略)
}
function clearInput() {
// クリア処理(省略)
}
</script>
クリア機能でも、clear()という名前が既存のdocument.clear()と重なり、自分で作った関数が呼び出されませんでした。こちらもclearInput()に変更して解決しました。
この経験から、関数名は「クリック」のような操作名よりも、「計算する」「入力を消す」のように、何をする関数なのか分かる名前にしようと思いました。
CSSで画面を整えるのにも苦戦した
CSSの調整にも時間がかかりました。基本的な書き方は練習していましたが、実際に画面を作ると、入力欄やボタン、結果をどう配置すれば使いやすくなるのか迷いました。
まずは余白と大きさから調整し、入力欄の説明を上に置き、2つのボタンをFlexboxで横並びにしました。計算結果では、1人あたりの金額を大きくして、重要な情報が目に入りやすいようにしました。
今回、CSSのプロパティの使い方を知っていても、それらを組み合わせて画面を整えるには、まだ練習が必要だと感じました。
9. まとめ
今回の割り勘計算アプリ制作では、これまで学んだJavaScriptの基礎を組み合わせて、実際に操作できるアプリを作りました。学習と復習を通して、コードの意味も少しずつ分かるようになり、入力値の取得からチェック、結果の表示までの流れを理解できました。
DOM操作では、「HTML要素を取得すること」と「入力された値を読み取ること」の違いが分かりました。また、文字の更新、スタイルの切り替え、入力欄のクリアも練習できました。新しくNumber.isInteger()とMath.ceil()を学び、letとconstの使い分けも整理できました。
機能はまだ少なく、調べたり質問したりする場面もありましたが、自分でコードを書きながら理解を深められたことが、今回一番の収穫です。今後も練習を続け、計算履歴などの機能も追加してみたいと思います。


