この7日間で作る電卓アプリのゴール
この 7 日間では、「ブラウザで動く電卓アプリ」を完成させながら、JavaScript の超基礎と、画面とプログラムをつなぐ考え方(DOM 操作)を身につけます。
電卓は初心者向けプロジェクトとして定番で、数値入力・演算処理・ボタンイベントなど、基本を一通り練習できます。
ここでは、次のようなシンプルな電卓を目標にします。
- 数字ボタンを押すと、画面に数字が表示される
- 「+ − × ÷」ボタンで計算式を作れる
- 「=」で結果を表示
- 「C」でクリア
HTML/CSS は最低限にして、「JavaScript の動き」に集中します。
1 日目:環境準備と「最小の電卓」を動かす
開発環境を整える
ブラウザ(Chrome など)と、メモ帳でもよいですが、できれば VS Code のようなエディタを用意すると書きやすくなります。
ここでは、ファイルを 1 つ作ってブラウザで開く方法で進めます。
- デスクトップなどに
calculatorフォルダを作る - その中に
index.htmlを作る index.htmlをブラウザで開く
最小の HTML 電卓画面を用意する
index.html に、まずは「数字を表示するだけ」の枠を書きます。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>電卓アプリ</title>
<style>
.calculator {
width: 200px;
margin: 40px auto;
padding: 10px;
border: 1px solid #333;
border-radius: 8px;
}
.display {
width: 100%;
height: 40px;
font-size: 24px;
text-align: right;
padding: 5px;
box-sizing: border-box;
margin-bottom: 10px;
border: 1px solid #999;
}
button {
width: 40px;
height: 40px;
margin: 2px;
font-size: 18px;
}
</style>
</head>
<body>
<div class="calculator">
<div id="display" class="display">0</div>
<button>1</button>
<button>2</button>
<button>3</button>
</div>
<script>
console.log("電卓アプリスタート");
</script>
</body>
</html>
ブラウザで開いて、「枠と 1〜3 のボタン」が見えていれば OK です。
まだボタンを押しても何も起こりませんが、これが「土台」になります。
JavaScript が動いているか確認する
ページを開いたまま、右クリック →「検証」→「Console(コンソール)」タブを開いてください。console.log("電卓アプリスタート"); の文字が表示されていれば、JavaScript がきちんと実行されています。
2 日目:DOM とイベントの基礎(ボタンを押したら表示が変わる)
DOM とは何か(重要なイメージ)
ブラウザの中では、HTML で書かれた要素が「DOM(Document Object Model)」というツリー構造として扱われ、JavaScript からはこの DOM を通して画面を操作します。
ざっくり言うと、「JavaScript から HTML の部品を触れるようにした仕組み」です。
画面の表示部分を JavaScript から触る
昨日の HTML に、JavaScript を少し書き足して、ボタンを押したら数字が表示に出るようにします。
<script>
const display = document.getElementById("display");
display.textContent = "123";
</script>
ポイントは document.getElementById("display") です。
これは「HTML の中から、id が display の要素を見つけて、変数 display に入れている」と解釈してください。textContent は、その中の文字を変えるプロパティです。
ボタンを押したときの処理(イベント)をつける
今はボタンに何も起こらないので、1 のボタンにだけ動きをつけてみます。
<button id="btn1">1</button>
と id を付けた上で、JavaScript を次のようにします。
<script>
const display = document.getElementById("display");
const btn1 = document.getElementById("btn1");
btn1.addEventListener("click", function() {
display.textContent = "1";
});
</script>
ここでの重要ポイントは 2 つです。
addEventListener("click", ...)は「クリックされたら、この中の処理を実行してね」という意味function() { ... }は「そのときに実行したい処理のかたまり(無名関数)」
「ボタン = イベントを持つもの」と捉えると理解しやすくなります。
2 日目の小さな課題
- 2 のボタンにも id を付けて、押したら表示が「2」になるようにしてみる
- 3 のボタンも同様に追加する
コードは多少ダサくて OK です。まずは「押したら変わる」を体で覚えましょう。
3 日目:数字ボタンを全部実装し、「入力する」という感覚を作る
数字ボタンを並べる
index.html のボタン部分を、0〜9 までと C(クリア)だけ用意してみます。
<div class="calculator">
<div id="display" class="display">0</div>
<div>
<button class="num-btn">7</button>
<button class="num-btn">8</button>
<button class="num-btn">9</button>
</div>
<div>
<button class="num-btn">4</button>
<button class="num-btn">5</button>
<button class="num-btn">6</button>
</div>
<div>
<button class="num-btn">1</button>
<button class="num-btn">2</button>
<button class="num-btn">3</button>
</div>
<div>
<button class="num-btn">0</button>
<button id="clear">C</button>
</div>
</div>
ここでは、数字ボタン全てに class="num-btn" を付けています。
これを使うと、「数字ボタン全部」をまとめて扱えます。
すべての数字ボタンに共通の処理をつける(重要)
JavaScript を次のように書きます。
<script>
const display = document.getElementById("display");
const numberButtons = document.querySelectorAll(".num-btn");
const clearButton = document.getElementById("clear");
let currentInput = "0";
numberButtons.forEach(function(button) {
button.addEventListener("click", function() {
const value = button.textContent;
if (currentInput === "0") {
currentInput = value;
} else {
currentInput = currentInput + value;
}
display.textContent = currentInput;
});
});
clearButton.addEventListener("click", function() {
currentInput = "0";
display.textContent = currentInput;
});
</script>
ここは電卓アプリの中でも重要な部分なので、少し深掘りします。
深掘り 1:querySelectorAll と forEach
document.querySelectorAll(".num-btn") は、「class が num-btn の要素を全部集めてくれる」メソッドです。
その結果は「配列のようなもの」なので、forEach で一つずつ順番に処理をつけられます。
buttonには、ループ中のそれぞれのボタン要素が入る- それぞれに
addEventListener("click", ...)を付与している
つまり「全数字ボタンに同じイベント処理をまとめてセットしている」わけです。
深掘り 2:currentInput の役割
currentInput は「今まで押した数字を文字列として保存している変数」です。
最初は "0" にしておき、数字を押すたびに末尾にくっつけていきます。
- 0 → 1 → 12 → 123 … のように増えていく
- 最初だけ
"0"を置き換え、それ以外は追加していくロジック
これで「数字を連続して押すと、表示に 2 桁以上が出る」という動きになります。
4 日目:演算子(+ − × ÷)を扱うための「状態」を考える
電卓が内部で持っている情報とは?
電卓の内部では、だいたい次のような情報を持つ必要があります。
- 1 つ目の数(最初のオペランド)
- 演算子(+ − × ÷ のどれか)
- 2 つ目の数(次のオペランド)
- 現在入力中の数(画面に出ている数)
これを JavaScript の変数で表現していきます。
基本となる「状態変数」を用意する
3 日目までのコードに、次の変数を追加します。
let currentInput = "0"; // 今入力している数(文字列)
let firstValue = null; // 最初の数
let operator = null; // 演算子("+", "-", "*", "/" など)
let waitingForSecond = false; // 次の数の入力待ちかどうか
JavaScriptこのように、電卓の中で「何を覚えておく必要があるか」を言語化し、変数として表現するのが大事です。
演算子ボタンを HTML に追加
HTML に演算子ボタンを追加します。
<div class="calculator">
<div id="display" class="display">0</div>
<div>
<button class="num-btn">7</button>
<button class="num-btn">8</button>
<button class="num-btn">9</button>
<button class="op-btn">/</button>
</div>
<div>
<button class="num-btn">4</button>
<button class="num-btn">5</button>
<button class="num-btn">6</button>
<button class="op-btn">*</button>
</div>
<div>
<button class="num-btn">1</button>
<button class="num-btn">2</button>
<button class="num-btn">3</button>
<button class="op-btn">-</button>
</div>
<div>
<button class="num-btn">0</button>
<button id="clear">C</button>
<button id="equals">=</button>
<button class="op-btn">+</button>
</div>
</div>
演算子ボタンには class="op-btn" を付けています。
これも querySelectorAll で全部まとめて扱います。
演算子が押されたときの処理(重要)
JavaScript 側に次を追加します。
const operatorButtons = document.querySelectorAll(".op-btn");
const equalsButton = document.getElementById("equals");
operatorButtons.forEach(function(button) {
button.addEventListener("click", function() {
const op = button.textContent;
if (firstValue === null) {
firstValue = Number(currentInput);
} else if (!waitingForSecond) {
const result = calculate(firstValue, Number(currentInput), operator);
firstValue = result;
display.textContent = result;
currentInput = String(result);
}
operator = op;
waitingForSecond = true;
});
});
JavaScriptここが少しややこしいですが、考え方を整理するとわかりやすくなります。
深掘り:演算子時のフロー
- 最初に演算子が押されたとき
firstValueがnull→ まだ計算開始前- 今の
currentInputを数値に変換して(Number())、firstValueに保存
- 2 回目以降の演算子が押されたとき
- すでに
firstValueがあるので、 firstValue (前の結果)とcurrentInput (新しい数)とoperator (前回の演算子)で計算- 結果を
firstValueとcurrentInputに反映して、画面に出す
- すでに
- 最後に、新しい
operatorを保存し、waitingForSecond = trueにする- これにより、「次に数字ボタンが押されたら、新しい数を入力し始める」モードに入る
この「状態の変化」を追いかけるのが、電卓実装の肝です。
5 日目:calculate 関数と「=」の処理を完成させる
計算ロジックを関数にまとめる(重要)
同じような計算処理は関数に切り出しておくと、コードが読みやすくなり、バグも減ります。
function calculate(a, b, op) {
if (op === "+") {
return a + b;
} else if (op === "-") {
return a - b;
} else if (op === "*") {
return a * b;
} else if (op === "/") {
return a / b;
} else {
return b;
}
}
JavaScriptここでのポイントは、「計算処理そのもの」と「いつ呼ぶか」を分けていることです。
電卓の「ボタンが押されたときの動き」が複雑でも、計算そのものはシンプルに保てます。
「=」ボタンの処理
equalsButton にイベントを追加します。
equalsButton.addEventListener("click", function() {
if (operator === null || waitingForSecond) {
return;
}
const secondValue = Number(currentInput);
const result = calculate(firstValue, secondValue, operator);
display.textContent = result;
currentInput = String(result);
firstValue = result;
operator = null;
waitingForSecond = false;
});
JavaScriptここでやっていることは次の通りです。
- 演算子が設定されていない、またはまだ 2 番目の数が入力されていない場合は、何もしない
- そうでなければ、
calculateを使って結果を出す - 表示を更新し、今後続けて計算できるように
firstValueを結果に更新する
これで、一連の流れ(「数 → 演算子 → 数 → =」)が繋がります。
6 日目:細かい改善とエラー対策、小数点対応に触れる
クリア処理をちゃんとリセットする
C ボタンで、全ての状態をきれいにリセットします。
3 日目の C 処理を、状態も含めて書き直します。
clearButton.addEventListener("click", function() {
currentInput = "0";
firstValue = null;
operator = null;
waitingForSecond = false;
display.textContent = currentInput;
});
JavaScriptこれで、「意味不明な状態」に迷い込んだ時でも C でリセットできるようになります。
0 で割った場合の簡単な対策(重要な考え方)
calculate 内で、割り算のときに 0 で割っていないかチェックします。
} else if (op === "/") {
if (b === 0) {
return "エラー";
}
return a / b;
}
JavaScript"エラー" を返した場合は、数値ではなく文字列になるので、equals 側で「次に数字を押されたらリセットする」といった制御を入れてもよいです。
ここでは、「異常なケースを想定してコードにしておく」という姿勢が重要です。
小数点ボタンを追加してみる(軽くチャレンジ)
HTML に「.」ボタンを追加します。
<button id="dot">.</button>
JavaScript では、dotButton のイベントを次のようにします。
const dotButton = document.getElementById("dot");
dotButton.addEventListener("click", function() {
if (waitingForSecond) {
currentInput = "0.";
waitingForSecond = false;
} else if (!currentInput.includes(".")) {
currentInput = currentInput + ".";
}
display.textContent = currentInput;
});
JavaScript- すでに
.を含んでいる場合は何もしない - 新しい数の入力待ちなら
0.から開始
これで「小数の入力」という現実の電卓に近い動きになります。
7 日目:コード全体の整理と、あなたなりの拡張
電卓アプリの全体像(シンプル版)
ここまで出てきた要素をまとめると、概ね次のような構成になります(多少省略しつつ要点だけ)。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>電卓アプリ</title>
<style>
.calculator {
width: 220px;
margin: 40px auto;
padding: 10px;
border: 1px solid #333;
border-radius: 8px;
background-color: #f9f9f9;
}
.display {
width: 100%;
height: 40px;
font-size: 24px;
text-align: right;
padding: 5px;
box-sizing: border-box;
margin-bottom: 10px;
border: 1px solid #999;
background-color: #fff;
}
button {
width: 40px;
height: 40px;
margin: 2px;
font-size: 18px;
}
</style>
</head>
<body>
<div class="calculator">
<div id="display" class="display">0</div>
<div>
<button class="num-btn">7</button>
<button class="num-btn">8</button>
<button class="num-btn">9</button>
<button class="op-btn">/</button>
</div>
<div>
<button class="num-btn">4</button>
<button class="num-btn">5</button>
<button class="num-btn">6</button>
<button class="op-btn">*</button>
</div>
<div>
<button class="num-btn">1</button>
<button class="num-btn">2</button>
<button class="num-btn">3</button>
<button class="op-btn">-</button>
</div>
<div>
<button class="num-btn">0</button>
<button id="dot">.</button>
<button id="clear">C</button>
<button class="op-btn">+</button>
</div>
<div>
<button id="equals">=</button>
</div>
</div>
<script>
const display = document.getElementById("display");
const numberButtons = document.querySelectorAll(".num-btn");
const operatorButtons = document.querySelectorAll(".op-btn");
const clearButton = document.getElementById("clear");
const equalsButton = document.getElementById("equals");
const dotButton = document.getElementById("dot");
let currentInput = "0";
let firstValue = null;
let operator = null;
let waitingForSecond = false;
function updateDisplay() {
display.textContent = currentInput;
}
numberButtons.forEach(function(button) {
button.addEventListener("click", function() {
const value = button.textContent;
if (waitingForSecond) {
currentInput = value;
waitingForSecond = false;
} else if (currentInput === "0") {
currentInput = value;
} else {
currentInput = currentInput + value;
}
updateDisplay();
});
});
dotButton.addEventListener("click", function() {
if (waitingForSecond) {
currentInput = "0.";
waitingForSecond = false;
} else if (!currentInput.includes(".")) {
currentInput = currentInput + ".";
}
updateDisplay();
});
function calculate(a, b, op) {
if (op === "+") {
return a + b;
} else if (op === "-") {
return a - b;
} else if (op === "*") {
return a * b;
} else if (op === "/") {
if (b === 0) {
return "エラー";
}
return a / b;
} else {
return b;
}
}
operatorButtons.forEach(function(button) {
button.addEventListener("click", function() {
const op = button.textContent;
const value = Number(currentInput);
if (firstValue === null) {
firstValue = value;
} else if (!waitingForSecond) {
const result = calculate(firstValue, value, operator);
currentInput = String(result);
firstValue = result;
updateDisplay();
}
operator = op;
waitingForSecond = true;
});
});
equalsButton.addEventListener("click", function() {
if (operator === null || waitingForSecond) {
return;
}
const secondValue = Number(currentInput);
const result = calculate(firstValue, secondValue, operator);
currentInput = String(result);
firstValue = result;
operator = null;
waitingForSecond = false;
updateDisplay();
});
clearButton.addEventListener("click", function() {
currentInput = "0";
firstValue = null;
operator = null;
waitingForSecond = false;
updateDisplay();
});
</script>
</body>
</html>
HTMLここまで書ければ、「HTML + CSS + JavaScript + DOM + イベント + 状態管理」を一通り体験できています。
7 日目の「あなたの工夫」を入れてみる
たとえば、こんな拡張が考えられます。
- ±(符号反転)ボタンを追加
- %ボタンを追加
- デザイン(色・フォント・ボタンサイズ)を自分好みに変える
- キーボード入力(数字キーでも操作できる)対応に挑戦
大事なのは、「自分で仕様を決めて、それをコードに落とす」というプロセスを楽しむことです。
この電卓で身につく本質と、次のステップ
この電卓アプリで、あなたは次の大事な感覚を手に入れています。
- 画面(HTML)とコード(JavaScript)を、DOM を通じてつなぐ感覚
- イベント駆動(ボタンが押された「とき」に処理する)という考え方
- アプリの「状態」を変数として設計し、状態遷移をコードで表す考え方


