JavaScriptで自動販売機を作る方法④|初心者向けWebアプリ作成

プログラミング

この記事は、プロモーションを含みます。

ハムケン先生
ハムケン先生

こんにちは!今回は「自動販売機Webアプリ作成」シリーズの完結編です。
この記事では、商品の在庫管理機能と、おつりを硬貨単位で表示する機能を追加していきます。

ちゃとらん
ちゃとらん

いよいよ、自動販売機が完成するんですね。
在庫管理って難しいんじゃないですか?

ハムケン先生
ハムケン先生

在庫管理って言葉だけ聞くと難しそうに思うけど、考え方は簡単だよ。
それでは、実際に作っていきましょう。

この記事で学べること

  • 在庫管理の考え方、作り方
  • おつりに関する考え方、作り方
  • 自動販売機を作ろうシリーズの最終的なソースコード

在庫管理を作ろう

在庫管理とは?

現実の自動販売機と同じように、ジュースがなくなったら売れないようにしたいですよね?
それを実現するには、プログラムの中で「各ジュースの在庫数(残り何本あるか)」をしっかり記録して、買われたら1本減らしていく、という処理が必要です。

どんな仕組みを作るの?

機能内容
在庫を記録するいちごソーダ:3本、ミルクティー:4本など
表示する「在庫:3本」など画面に見えるようにする
減らす商品が買われたら、在庫を1つ減らす
売り切れ表示在庫が0になったら「売り切れ」と表示して、購入できないようにする

「在庫管理」を処理フローに追加してみよう

在庫を減らし、在庫が0個になったら売り切れ表示をして、在庫の表示を更新します。

HTMLの修正

在庫を表示するためのspanタグを追加します。HTMLを以下のように修正してください。

<div class="juice">
  <div class="juice-name">
    いちごソーダ - 150円
    <span id="stock-strawberry">(在庫: 3)</span>
  </div>
  <button id="btn-strawberry" onclick="buyJuice('いちごソーダ', 150, 'strawberry')">購入</button>
</div>

<div class="juice">
  <div class="juice-name">
    ももジュース - 130円
    <span id="stock-peach">(在庫: 4)</span>
  </div>
  <button id="btn-peach" onclick="buyJuice('ももジュース', 130, 'peach')">購入</button>
</div>

<div class="juice">
  <div class="juice-name">
    ミルクティー - 100円
    <span id="stock-milk">(在庫: 5)</span>
  </div>
  <button id="btn-milk" onclick="buyJuice('ミルクティー', 100, 'milk')">購入</button>
</div>

buyJuice関数の第三引数に、在庫数をチェックするキーを追加しています。

JavaScriptの修正

各ジュースの在庫数を管理するため、以下のようなJavaScriptオブジェクトを作成します。これにより、在庫がない商品の購入を防ぐ処理が可能になります。

// 商品ごとの在庫を管理するオブジェクト
const stock = {
  strawberry: 3,
  peach: 4,
  milk: 5
};


/** ジュースの購入ボタンをクリックされた時の処理 **/
function buyJuice(name, price, key) {
  const moneyInput = document.getElementById('money');
  const message = document.getElementById('message');
  const money = Number(moneyInput.value);
  
  // 在庫チェック
  if (stock[key] <= 0) {
    message.textContent = `${name} は売り切れだよ!`;
    return;
  }
  
  // 購入制限チェック
  if (money < price) {
    message.textContent = `ごめんね…あと ${price - money} 円たりないよ。`;
    return;
  }
  
  stock[key]--;
  let zankin = moneyInput.value - price;
  moneyInput.value = zankin;
  
  // 在庫数を更新
  document.getElementById(`stock-${key}`).textContent = `(在庫: ${stock[key]})`;

  // 在庫が0になったらボタン無効化
  if (stock[key] === 0) {
    document.getElementById(`btn-${key}`).disabled = true;
    document.getElementById(`btn-${key}`).textContent = "売り切れ";
  }
  
  alert(`${name}:${price}円を購入しました♪`);
  
}

① 商品の在庫を管理するためのオブジェクトを追加

const stock = {
  strawberry: 5,
  peach: 3,
  milk: 2
};
  • それぞれの商品に対して「残り何本あるか」を管理するための在庫オブジェクトを作成。
  • key(例: "strawberry")を使って在庫数にアクセスします。

② 在庫チェックの処理を追加

if (stock[key] <= 0) {
  message.textContent = `${name} は売り切れだよ!`;
  return;
}
  • 商品の在庫が0以下なら購入をキャンセルし、「売り切れ」メッセージを表示します。
  • return; でこの時点で処理を終了しています。

③ 購入成功時に在庫数を1つ減らす処理を追加

stock[key]--;
  • 商品が1本売れたので、在庫数を1つ減らします。

④ 在庫表示欄のテキストを更新する処理を追加

document.getElementById(`stock-${key}`).textContent = `(在庫: ${stock[key]})`;
  • 画面上に表示されている「在庫:◯本」の数字を、現在の在庫数に書き換えます。
  • HTML側で id="stock-strawberry" などと記述しておく必要があります。

⑤ 在庫が0になったら購入ボタンを無効化する処理を追加

if (stock[key] === 0) {
  document.getElementById(`btn-${key}`).disabled = true;
  document.getElementById(`btn-${key}`).textContent = "売り切れ";
}
  • 在庫が0になったタイミングで、購入ボタンを押せなくするdisabled = true)。
  • ボタンのラベルも「売り切れ」に変更して、ユーザーにわかりやすく表示します。

動きを見てみよう

HTMLファイルをダブルクリックして、ブラウザで表示してください。
商品ごとに在庫数が表示されています。

入金額に「1000」を入力し、「いちごソーダ」の購入ボタンをクリックしてください。
在庫数が「2]に減っています。

このまま、あと2回購入して在庫を0にしてください。
購入ボタンが売り切れボタンに変わり、ボタンがクリック出来ないようになりました。

これで、在庫管理の実装が完了です。

おつりを小銭で受け取ろう

おつりボタンを作成し、おつりボタンが押されたら、金額が大きい順に、硬貨の枚数が表示される処理を追加します。

HTMLの修正

ここまでの記事で追加・修正してきた内容をすべてまとめた、完成版のHTMLコードを以下に掲載します。ファイルを一から作成する際にもご活用ください。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>もふもふ自動販売機</title>
  <style>
    body {
      background-color: #fffaf5;
      font-family: "Rounded Mplus 1c", "Hiragino Maru Gothic ProN", sans-serif;
      text-align: center;
      padding: 40px;
    }

    .vending-machine {
      display: inline-block;
      background: #ffeef0;
      border: 5px solid #f7c5cc;
      border-radius: 20px;
      padding: 30px 40px;
      box-shadow: 0 5px 15px rgba(0,0,0,0.1);
    }

    h2 {
      color: #d35d7c;
      margin-bottom: 20px;
      font-size: 28px;
    }

    .money-input {
      margin-bottom: 25px;
    }

    input[type="number"] {
      padding: 8px 10px;
      font-size: 16px;
      border: 2px solid #f7c5cc;
      border-radius: 10px;
      width: 150px;
    }

    .juice {
      background-color: #fff;
      border: 2px solid #f7c5cc;
      border-radius: 12px;
      padding: 15px;
      margin: 15px 0;
    }

    .juice-name {
      font-weight: bold;
      font-size: 18px;
      color: #333;
    }

    button {
      margin-top: 10px;
      padding: 8px 20px;
      font-size: 16px;
      background-color: #ffd7e0;
      border: none;
      border-radius: 12px;
      cursor: pointer;
      transition: background-color 0.3s;
    }

    button:hover {
      background-color: #f7b9c5;
    }

    #message {
      margin-top: 20px;
      color: #b85c75;
      font-weight: bold;
    }
    
    .vending-machine, .change-area {
    	display: inline-block;
    }
    
    .money-input, .change-button {
    	display: inline-block;
    }
  </style>
</head>
<body>

  <div class="vending-machine">
    <h2>もふもふ自動販売機</h2>

    <div class="money-input">
      <label for="money">お金を入れてください:</label><br>
      <input type="number" id="money" placeholder="例:200" min=0 max=1000 />
    </div>
    
    <div class="change-button">
      <button id="btn-change" onclick="showChange()">おつり</button>
    </div>
    
    <p id="message">

    <div class="juice">
      <div class="juice-name">
        いちごソーダ - 150円
        <span id="stock-strawberry">(在庫: 3)</span>
      </div>
      <button id="btn-strawberry" onclick="buyJuice('いちごソーダ', 150, 'strawberry')">購入</button>
    </div>

    <div class="juice">
      <div class="juice-name">
        ももジュース - 130円
        <span id="stock-peach">(在庫: 4)</span>
      </div>
      <button id="btn-peach" onclick="buyJuice('ももジュース', 130, 'peach')">購入</button>
    </div>

    <div class="juice">
      <div class="juice-name">
        ミルクティー - 100円
        <span id="stock-milk">(在庫: 5)</span>
      </div>
      <button id="btn-milk" onclick="buyJuice('ミルクティー', 100, 'milk')">購入</button>
    </div>

    <p id="message"></p>
  </div>
  
  <!-- おつり表示エリア -->
<div class="change-area">
  <h3>おつり</h3>
  <div class="coin" id="coin-1000">
    1000円札
    <span id="change-1000">× 0</span>
  </div>
  <div class="coin" id="coin-500">
    500円硬貨
    <span id="change-500">× 0</span>
  </div>
  <div class="coin" id="coin-100">
    100円硬貨
    <span id="change-100">× 0</span>
  </div>
  <div class="coin" id="coin-50">
    50円硬貨
    <span id="change-50">× 0</span>
  </div>
  <div class="coin" id="coin-10">
    10円硬貨
    <span id="change-10">× 0</span>
  </div>
</div>

  <script src="vendingMachine.js"></script>

</body>
</html>

①おつり表示エリアのレイアウトを設定

.vending-machine, .change-area {
    display: inline-block;
}
  • 自動販売機の右側におつりの表示クラスを配置する。

②おつりボタンのレイアウトを設定

.money-input, .change-button {
    display: inline-block;
}
  • 入金額の右側におつりボタンを配置する。

③おつりボタンを作成

<div class="change-button">
  <button id="btn-change" onclick="showChange()">おつり</button>
</div>
  • クリックされたら関数「showChange()」が呼び出される。

④おつり表示エリアの作成

<!-- おつり表示エリア -->
<div class="change-area">
<h3>おつり</h3>
<div class="coin" id="coin-1000">
1000円札
<span id="change-1000">× 0</span>
</div>
<div class="coin" id="coin-500">
500円硬貨
<span id="change-500">× 0</span>
</div>
<div class="coin" id="coin-100">
100円硬貨
<span id="change-100">× 0</span>
</div>
<div class="coin" id="coin-50">
50円硬貨
<span id="change-50">× 0</span>
</div>
<div class="coin" id="coin-10">
10円硬貨
<span id="change-10">× 0</span>
</div>
</div>

  • おつりの表示エリアでは、各金種ごとに「何枚のおつりがあるか」を表示します。
  • たとえば「100円硬貨 × 2」と表示されるように、各行にIDを割り当て、JavaScriptから値を動的に反映させる構造になっています。

JavaScriptの修正

ここまでの記事で追加・修正してきた内容をすべてまとめた、完成版のJavaScriptコードを以下に掲載します。ファイルを一から作成する際にもご活用ください。

// 商品ごとの在庫を管理するオブジェクト
const stock = {
  strawberry: 3,
  peach: 4,
  milk: 5
};


/** ジュースの購入ボタンをクリックされた時の処理 **/
function buyJuice(name, price, key) {
  const moneyInput = document.getElementById('money');
  const message = document.getElementById('message');
  const money = Number(moneyInput.value);
  
  // 在庫チェック
  if (stock[key] <= 0) {
    message.textContent = `${name} は売り切れだよ!`;
    return;
  }
  
  // 購入制限チェック
  if (money < price) {
    message.textContent = `ごめんね…あと ${price - money} 円たりないよ。`;
    return;
  }
  
  stock[key]--;
  let zankin = moneyInput.value - price;
  moneyInput.value = zankin;
  
  // 在庫数を更新
  document.getElementById(`stock-${key}`).textContent = `(在庫: ${stock[key]})`;

  // 在庫が0になったらボタン無効化
  if (stock[key] === 0) {
    document.getElementById(`btn-${key}`).disabled = true;
    document.getElementById(`btn-${key}`).textContent = "売り切れ";
  }
  
  alert(`${name}:${price}円を購入しました♪`);
  
}

/** 入力金額チェック(0未満・1000超え) **/
document.addEventListener('DOMContentLoaded', function () {
  const moneyInput = document.getElementById('money');
  const message = document.getElementById('message');

  moneyInput.addEventListener('input', function () {
    const money = Number(moneyInput.value);

    if (money < 0) {
      message.textContent = "0円未満は入力できないよ。";
      moneyInput.value = ""
    } else if (money > 1000) {
      message.textContent = "1000円を超える金額は入力できないよ。";
      moneyInput.value = ""
    } else {
      message.textContent = "";
    }
  });
});

// おつりを表示する関数
function showChange() {
  const moneyInput = document.getElementById("money");
  let change = Number(moneyInput.value);

  // 各硬貨の枚数を計算(大きい金額優先)
  const denominations = [1000, 500, 100, 50, 10];
  const changeCounts = {
    1000: 0,
    500: 0,
    100: 0,
    50: 0,
    10: 0
  };

  for (let yen of denominations) {
    changeCounts[yen] = Math.floor(change / yen);
    change -= yen * changeCounts[yen];
  }

  // 表示を更新
  for (let yen of denominations) {
    document.getElementById(`change-${yen}`).textContent = `× ${changeCounts[yen]}`;
  }

  // お金欄をリセット
  moneyInput.value = 0;
  document.getElementById("message").textContent = "おつりを受け取ったよ♪";
}

① 入力された金額を取得する

const moneyInput = document.getElementById("money");
let change = Number(moneyInput.value);
  • ユーザーが入力した金額(残っているお金)を取得します。
  • Number() で数値として使えるようにします。
  • この change という変数が、今から分けていく「おつりの合計金額」です。

② おつりの金種(硬貨)とカウントの準備

const denominations = [1000, 500, 100, 50, 10];
const changeCounts = {
  1000: 0,
  500: 0,
  100: 0,
  50: 0,
  10: 0
};
  • denominations は使える金種を大きい順に並べた配列です。
  • changeCounts は、各金種が**何枚使われたかを記録する箱(オブジェクト)**です。
  • 最初はすべて0枚で初期化しておきます。

③ 大きい金種から順におつりを計算

for (let yen of denominations) {
  changeCounts[yen] = Math.floor(change / yen);
  change -= yen * changeCounts[yen];
}
  • おつりを大きい金額から優先的に分けていく処理です。
  • 例えば、change が 1370円 の場合:
    • 1000円札 ×1(残370円)
    • 100円硬貨 ×3(残70円)
    • 50円硬貨 ×1(残20円)
    • 10円硬貨 ×2(残0円)
  • Math.floor() は小数点以下を切り捨てて、整数だけを使います。

④ 画面上におつりの枚数を表示

for (let yen of denominations) {
  document.getElementById(`change-${yen}`).textContent = `× ${changeCounts[yen]}`;
}
  • HTMLの中の <span id="change-1000"> などの部分に、計算した枚数を表示します。
  • たとえば × 3 と表示されれば「その金種のおつりが3枚」という意味になります。

⑤ 入金額をリセットし、メッセージを表示

moneyInput.value = 0;
document.getElementById("message").textContent = "おつりを受け取ったよ♪";
  • おつりを受け取ったら、残金(money)は 0円 にリセット。
  • ユーザーに向けて「おつりを受け取ったよ♪」という可愛いメッセージを表示します。

動きを見てみよう

HTMLファイルをダブルクリックして、ブラウザで表示してください。
自動販売機の右下に、おつりの表示エリアが作成されています。

入金額に「1000」を入力し、おつりボタンをクリックしてください。
「1000円札」が1と更新されています。

もう一度、入金額に「1000」を入力し、ももジュースの購入ボタンをクリックしてください。
その後、おつりボタンをクリックしてください。

1000円札以外の硬貨の数字が870円分、更新されていることが確認できました。

ハムケン先生
ハムケン先生

自動販売機Webアプリの制作お疲れさまでした。
これで、全工程が終了しました。

ちゃとらん
ちゃとらん

ありがとうございました。
Webアプリの作り方が分かるようになりました。

ハムケン先生
ハムケン先生

今後も、ツールやゲームのWebアプリを中心に、作り方を公開していこうと思います。是非、他の記事も読んでくださいね♪

ここまで一緒にWebアプリを作ってくれてありがとうございました。
自動販売機のWebアプリだけでは、使っていないJavaScriptの機能は沢山あります。

こちらのJavaScriptに関する書籍を読めば、もっとJavaScriptに詳しくなれるので
よかったら読んでみてください。


コメント

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