商品やサービスの料金ページで、選んだオプションに合わせて合計金額が自動で変わる料金シミュレーター。チェックボックス、ラジオボタン、セレクトボックスを組み合わせたものも、数十行のコードで作れます。
この記事では、ログサウナのオプション料金を例に、コピペで動くコード(jQueryなしのJavaScript版とjQuery版)と、作るときに気をつけたいところをまとめます。
以前この記事で紹介していたコードには、スマホでスクロールしただけで選択されてしまう、キーボードで操作できない、といった問題がありました。2026年10月に、本物のフォーム部品を使う書き方に全面的に書き直しています。変えた点は記事の後半にまとめました。
作り方の考え方:毎回「全部足し直す」
料金シミュレーターでいちばん大事なのは、選択が変わるたびに、いま選ばれているものを全部足し直すことです。
「チェックを入れたら足す、外したら引く」という書き方もできますが、この方法だと、リセットやラジオボタンの切り替えなどで足し引きが1回ずれただけで、合計がずっとずれたままになります。毎回全部足し直せば、どんな順番で操作しても、表示される合計は必ず画面の選択と一致します。
もう1つは、チェックボックスやラジオボタンには本物のinputを使うことです。divをクリックで切り替える作りにすると、キーボードで操作できず、フォームとして送信もできません。本物のinputなら、見た目はCSSで自由に変えられます。
コピペで動くコード(JavaScript版)
下のHTMLを1つのファイルとして保存し、ブラウザで開けば動きます。jQueryは使いません。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>料金シミュレーター</title>
<style>
.sim { max-width: 560px; margin: 40px auto; padding: 0 16px; font-family: sans-serif; }
.sim fieldset { border: 0; padding: 0; margin: 0 0 24px; }
.sim legend { width: 100%; padding-bottom: 6px; margin-bottom: 10px; border-bottom: 1px solid #333; font-weight: bold; }
.sim label { display: inline-flex; align-items: center; gap: 6px; margin: 0 16px 8px 0; cursor: pointer; }
.sim label:has(input:checked) { font-weight: bold; }
.sim select { width: 100%; padding: 10px; font-size: 1rem; }
.sim__total { font-size: 1.5rem; font-weight: bold; }
</style>
</head>
<body>
<form class="sim" id="simulator">
<fieldset>
<legend>ログの形</legend>
<label><input type="radio" name="log" value="丸ログ" data-price="1000" checked> 丸ログ ¥1,000</label>
<label><input type="radio" name="log" value="角ログ" data-price="2000"> 角ログ ¥2,000</label>
</fieldset>
<fieldset>
<legend>ストーブ</legend>
<label><input type="radio" name="stove" value="電気ストーブ" data-price="10000"> 電気ストーブ ¥10,000</label>
<label><input type="radio" name="stove" value="薪ストーブ" data-price="20000"> 薪ストーブ ¥20,000</label>
</fieldset>
<fieldset>
<legend>オプション(複数選べます)</legend>
<label><input type="checkbox" name="option" value="照明" data-price="5000"> 照明 ¥5,000</label>
<label><input type="checkbox" name="option" value="外壁塗装" data-price="50000"> 外壁塗装 ¥50,000</label>
<label><input type="checkbox" name="option" value="ベンチ" data-price="30000"> ベンチ ¥30,000</label>
</fieldset>
<fieldset>
<legend>運搬費</legend>
<select name="transport">
<option value="" data-price="0">地域をお選びください</option>
<option value="地域1" data-price="2000">地域1 ¥2,000</option>
<option value="地域2" data-price="3000">地域2 ¥3,000</option>
<option value="地域3" data-price="4000">地域3 ¥4,000</option>
</select>
</fieldset>
<p class="sim__total">合計 <output id="total">0</output> 円</p>
<button type="reset">リセット</button>
</form>
<script>
const form = document.getElementById('simulator');
const totalEl = document.getElementById('total');
function calcTotal() {
let total = 0;
// チェックが入っているラジオボタンとチェックボックス
form.querySelectorAll('input:checked').forEach((input) => {
total += Number(input.dataset.price) || 0;
});
// セレクトボックスで選ばれている項目
form.querySelectorAll('select').forEach((select) => {
total += Number(select.selectedOptions[0].dataset.price) || 0;
});
totalEl.textContent = total.toLocaleString('ja-JP');
}
// どの部品が変わっても、フォーム全体で受け取って計算し直す
form.addEventListener('change', calcTotal);
// reset イベントは値が戻る「前」に発生するので、戻ったあとに計算する
form.addEventListener('reset', () => {
setTimeout(calcTotal);
});
calcTotal();
</script>
</body>
</html>
コードのポイント
金額はdata-priceに持たせる。各選択肢にdata-price="5000"のように金額を書いておき、JavaScriptではinput.dataset.priceで読み取ります。金額を変えたいときはHTMLだけ直せば済みます。valueには、あとでフォームで送るときに分かりやすい名前(「照明」など)を入れておきます。
イベントはフォームに1つだけ。チェックボックスやラジオボタン、セレクトボックスで起きたchangeイベントは、親のフォームまで伝わります(バブリング)。なので、部品ごとにイベントを付けなくても、form.addEventListener('change', ...) 1つで全部受け取れます。選択肢を増やしても、JavaScriptを直す必要はありません。
リセットは「戻ったあと」に計算する。resetイベントは、フォームの値が初期状態に戻る前に発生します。そのままcalcTotal()を呼ぶと、リセット前の金額で計算してしまうので、setTimeoutで値が戻ってから計算しています。
3桁区切りはtoLocaleString('ja-JP')。123456が123,456になります。引数を省略するとブラウザの言語設定に従うので、'ja-JP'を指定しておくと表示がそろいます。
ラジオボタンの初期値。必ずどれかを選んでもらう項目は、最初の選択肢にcheckedを付けておくと、合計が最初から意味のある金額になります。
jQuery版
すでにjQueryを使っているサイト(WordPressのテーマなど)なら、<script>の中身を次のように書けます。HTMLとCSSは上と同じです。
jQuery(function ($) {
const $form = $('#simulator');
function calcTotal() {
let total = 0;
// チェックが入っている input と、選ばれている option
$form.find('input:checked, option:selected').each(function () {
total += Number($(this).data('price')) || 0;
});
$('#total').text(total.toLocaleString('ja-JP'));
}
$form.on('change', calcTotal);
$form.on('reset', function () {
setTimeout(calcTotal);
});
calcTotal();
});
jQuery 3でも、2026年1月に公開されたjQuery 4でも動く書き方にしています(jQueryで非推奨になっている.change()のような省略形は使わず、.on('change', ...)で書いています)。
よくあるカスタマイズ
数量(人数・個数)を掛ける
「1人3,000円 × 人数」のように数量を掛けたい場合は、type="number"の入力欄を使います。
<fieldset>
<legend>人数</legend>
<label><input type="number" name="people" value="1" min="1" max="20" data-price="3000"> 人(1人 ¥3,000)</label>
</fieldset>
calcTotal()に次の処理を足し、数字を打つたびに計算されるようinputイベントも受け取ります。
// 数量 × 単価
form.querySelectorAll('input[type="number"]').forEach((input) => {
total += (Number(input.dataset.price) || 0) * (Number(input.value) || 0);
});
form.addEventListener('input', calcTotal);
税込で表示する
金額を税抜で持っている場合は、最後に税率を掛けます。1円未満の端数の扱い(切り捨て・四捨五入)は、お店のルールに合わせてください。
const taxIncluded = Math.floor(total * 1.1); // 切り捨て
totalEl.textContent = taxIncluded.toLocaleString('ja-JP');
計算結果をお問い合わせフォームに送る
シミュレーターの結果を、そのまま見積もりの依頼として送ってもらいたい場合は、フォームに合計金額用のhiddenの欄を作り、calcTotal()の最後で値を入れます。
document.getElementById('total-price').value = total;
Contact Form 7を使っている場合は、フォームに[hidden total-price id:total-price]のようなタグを置き、メール本文に[total-price]を書けば、計算結果がメールに入ります。
注意:画面の金額は、あくまで目安の表示です。
data-priceはブラウザから書き換えられるので、送られてきた金額をそのまま請求額として扱わず、受け取った側で選択内容から計算し直してください。
以前の版から変えた点
以前のコードを使っている方は、次の点を見直してみてください。
| 以前のコード | 問題 | 今のコード |
|---|---|---|
divをクリックで切り替えていた |
キーボードで操作できない。フォームで送れない | 本物のinput type="radio" / checkbox |
スマホではtouchstartで反応させていた |
選択肢の上から指でスクロールし始めただけで選択されてしまう | changeイベント(スマホでもそのまま動く) |
| 選んだら足す・外したら引く | 足し引きが1回ずれると合計がずれたままになる | 毎回すべて足し直す |
変数をletなしで宣言していた |
グローバル変数になり、ほかのスクリプトとぶつかる | 関数の中でletで宣言 |
| リセット後に合計を計算し直していなかった | 内部の金額と表示がずれることがある | resetのあとに計算し直す |
ほかの作り方
選択肢の組み合わせが多い、地域や数量で単価が変わるなど、料金表が複雑になってくると、手書きのJavaScriptでは管理が大変になります。WordPressなら、計算式を画面で組めるプラグインCalculated Fields Formを使う方法もあります。
まとめ
- チェックボックス・ラジオボタン・セレクトボックスには本物のフォーム部品を使い、金額は
data-priceに持たせる - 合計は、選択が変わるたびに全部足し直す。足し引きで管理するとずれる
- イベントはフォームに1つ。
resetは値が戻ってから計算し直す - 3桁区切りは
toLocaleString('ja-JP') - 画面の金額は目安。送られてきた金額は受け取った側で計算し直す
