JavaScript | 1 日 60 分 × 7 日アプリ学習:電卓アプリ編

APP JavaScript
スポンサーリンク
  1. この7日間で作る電卓アプリのゴール
  2. 1 日目:環境準備と「最小の電卓」を動かす
    1. 開発環境を整える
    2. 最小の HTML 電卓画面を用意する
    3. JavaScript が動いているか確認する
  3. 2 日目:DOM とイベントの基礎(ボタンを押したら表示が変わる)
    1. DOM とは何か(重要なイメージ)
    2. 画面の表示部分を JavaScript から触る
    3. ボタンを押したときの処理(イベント)をつける
    4. 2 日目の小さな課題
  4. 3 日目:数字ボタンを全部実装し、「入力する」という感覚を作る
    1. 数字ボタンを並べる
    2. すべての数字ボタンに共通の処理をつける(重要)
  5. 4 日目:演算子(+ − × ÷)を扱うための「状態」を考える
    1. 電卓が内部で持っている情報とは?
    2. 基本となる「状態変数」を用意する
    3. 演算子ボタンを HTML に追加
    4. 演算子が押されたときの処理(重要)
  6. 5 日目:calculate 関数と「=」の処理を完成させる
    1. 計算ロジックを関数にまとめる(重要)
    2. 「=」ボタンの処理
  7. 6 日目:細かい改善とエラー対策、小数点対応に触れる
    1. クリア処理をちゃんとリセットする
    2. 0 で割った場合の簡単な対策(重要な考え方)
    3. 小数点ボタンを追加してみる(軽くチャレンジ)
  8. 7 日目:コード全体の整理と、あなたなりの拡張
    1. 電卓アプリの全体像(シンプル版)
    2. 7 日目の「あなたの工夫」を入れてみる
  9. この電卓で身につく本質と、次のステップ

この7日間で作る電卓アプリのゴール

この 7 日間では、「ブラウザで動く電卓アプリ」を完成させながら、JavaScript の超基礎と、画面とプログラムをつなぐ考え方(DOM 操作)を身につけます。
電卓は初心者向けプロジェクトとして定番で、数値入力・演算処理・ボタンイベントなど、基本を一通り練習できます。

ここでは、次のようなシンプルな電卓を目標にします。

  • 数字ボタンを押すと、画面に数字が表示される
  • 「+ − × ÷」ボタンで計算式を作れる
  • 「=」で結果を表示
  • 「C」でクリア

HTML/CSS は最低限にして、「JavaScript の動き」に集中します。


1 日目:環境準備と「最小の電卓」を動かす

開発環境を整える

ブラウザ(Chrome など)と、メモ帳でもよいですが、できれば VS Code のようなエディタを用意すると書きやすくなります。
ここでは、ファイルを 1 つ作ってブラウザで開く方法で進めます。

  1. デスクトップなどに calculator フォルダを作る
  2. その中に index.html を作る
  3. 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 つです。

  1. addEventListener("click", ...) は「クリックされたら、この中の処理を実行してね」という意味
  2. 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:querySelectorAllforEach

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

ここが少しややこしいですが、考え方を整理するとわかりやすくなります。

深掘り:演算子時のフロー

  1. 最初に演算子が押されたとき
    • firstValuenull → まだ計算開始前
    • 今の currentInput を数値に変換して(Number())、firstValue に保存
  2. 2 回目以降の演算子が押されたとき
    • すでに firstValue があるので、
    • firstValue (前の結果)currentInput (新しい数)operator (前回の演算子) で計算
    • 結果を firstValuecurrentInput に反映して、画面に出す
  3. 最後に、新しい 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 を通じてつなぐ感覚
  • イベント駆動(ボタンが押された「とき」に処理する)という考え方
  • アプリの「状態」を変数として設計し、状態遷移をコードで表す考え方

タイトルとURLをコピーしました