select・checkbox・radioは、どれも「あらかじめ用意した選択肢から選んでもらう」ためのフォーム部品で、初期値はselectならselected属性、checkboxとradioならchecked属性で決めます。 値の取得は、JavaScriptならvalue(selectとradio)とchecked(checkbox)、まとめて取るならFormDataを使います。

この記事では、3つの部品を「書き方・初期値・取得・設定・変更イベント・送信される値」の6つの観点で横に並べて比べます。コードはすべてjQueryなしの素のJavaScriptで統一し、Playwright 1.56.0 に同梱の Chromium 141 で実際に動かした結果をそのまま載せています。「チェックを外したcheckboxがサーバーに届かない」「valueを書き忘れたらonが届いた」といった、つまずきやすい挙動も実行結果で確かめます。

select・checkbox・radioの早見表

まずは3つの部品を1枚で比べます。迷ったときはこの表だけ見れば、書き方から送信される値までたどれます。

観点 select(プルダウン) checkbox(チェックボックス) radio(ラジオボタン)
選べる数 1つ(multipleで複数) 0個以上(それぞれ独立) 同じnameの中で1つ
HTMLの書き方 <select name>の中に<option value> <input type="checkbox" name value> <input type="radio" name value>を同じnameで並べる
初期値の属性 <option selected> checked checked
初期値を書かないとき 先頭のoptionが選ばれる 未チェック どれも未選択
JavaScriptで取得 select.value input.checked(true / false) form.elements.名前.value
複数の値を取得 [...select.selectedOptions] querySelectorAll(":checked") (1つだけ)
FormDataで取得 fd.get("名前")、複数はfd.getAll("名前") fd.getAll("名前") fd.get("名前")
JavaScriptで設定 select.value = "m" input.checked = true form.elements.名前.value = "red"
変更イベント change change change(選ばれた側だけ)
送信される値 選ばれたoptionのvalue チェックありのときだけvalue 選ばれた1つのvalue
valueを省略すると optionの表示テキスト 文字列on 文字列on
何も選ばれていないとき (単一は必ず何か選ばれる) 名前ごと送信されない 名前ごと送信されない
必須にする required+先頭をvalue="" required(その1個が必須) グループのどれか1つにrequired

使い分けの目安は次のとおりです。

選んでもらう内容 向いている部品 理由
2〜5個から1つ radio 選択肢が最初から全部見える
6個以上から1つ select 場所を取らない
複数を選ぶ checkbox 選んだ状態がひと目でわかる
はい・いいえの1項目 checkbox 1個 クリック1回で切り替えられる
数十個から複数 checkboxの一覧(またはselect multiple) select multipleはCtrlキー操作が必要になる

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「HTML select・checkbox・radioの書き方|初期値と値取得」

コピペで動く完成形のフォーム

3つの部品を1つのフォームにまとめた見本です。この記事の実行結果は、すべてこのHTMLを使っています。ファイル名をform.htmlとして保存すれば、そのまま開けます。

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>select・checkbox・radio の見本</title>
</head>
<body>
<form id="order" action="/echo" method="post">
  <p>
    <label for="size">サイズ</label>
    <select id="size" name="size">
      <option value="">選んでください</option>
      <option value="s">S</option>
      <option value="m" selected>M</option>
      <option value="l">L</option>
    </select>
  </p>

  <p>
    <label for="tags">タグ(複数可)</label>
    <select id="tags" name="tags" multiple size="3">
      <option value="new" selected>新着</option>
      <option value="sale">セール</option>
      <option value="gift" selected>ギフト</option>
    </select>
  </p>

  <fieldset>
    <legend>色(1つ選ぶ)</legend>
    <label><input type="radio" name="color" value="red"> 赤</label>
    <label><input type="radio" name="color" value="blue" checked> 青</label>
    <label><input type="radio" name="color" value="green"> 緑</label>
  </fieldset>

  <fieldset>
    <legend>オプション(複数可)</legend>
    <label><input type="checkbox" name="option" value="wrap" checked> ラッピング</label>
    <label><input type="checkbox" name="option" value="card"> メッセージカード</label>
    <label><input type="checkbox" name="option" value="bag" checked> 手さげ袋</label>
  </fieldset>

  <p>
    <input type="hidden" name="mail" value="0">
    <label><input type="checkbox" name="mail" value="1"> お知らせメールを受け取る</label>
  </p>

  <p>
    <label><input type="checkbox" name="agree"> 利用規約に同意する</label>
  </p>

  <button type="submit">送信</button>
  <button type="reset">リセット</button>
</form>

<script>
const form = document.querySelector("#order");
form.addEventListener("change", (e) => {
  console.log("change:", e.target.name, "=",
    e.target.type === "checkbox" ? e.target.checked : e.target.value);
});
</script>
</body>
</html>

このフォームには、あとで説明する3つの仕掛けを入れてあります。

  • mail:同じ名前のhiddenを前に置き、未チェックでも値が届くようにしている
  • agree:わざとvalueを書いていない(onが届くことを確かめるため)
  • tags:select multipleで初期値を2つ選んでいる

selectの書き方と初期値

基本の形

selectはoptionを並べて作ります。サーバーに届くのはoptionのvalueで、画面に見えるのはタグの間のテキストです。

<label for="size">サイズ</label>
<select id="size" name="size">
  <option value="">選んでください</option>
  <option value="s">S</option>
  <option value="m" selected>M</option>
  <option value="l">L</option>
</select>
属性 付ける場所 はたらき
name select 送信するときの項目名。書かないと送信されない
value option 送信される値。省略すると表示テキストが値になる
selected option 初期値にする
disabled option / select 選べなくする。選ばれていても送信されない
multiple select 複数選択にする
size select 一度に見せる行数
required select 必須にする

初期値はoptionにselectedを付ける

初期値にしたいoptionにselectedを書きます。selectedをどこにも書かなければ、先頭のoptionが選ばれた状態になります。

よく見る「選んでください」という案内は、先頭にvalue=""のoptionを置く形で作ります。requiredと組み合わせると、案内のまま送信しようとしたときにブラウザーが止めてくれます。

<select name="size" required>
  <option value="">選んでください</option>
  <option value="s">S</option>
  <option value="m">M</option>
</select>

グループ分けはoptgroup

選択肢が多いときはoptgroupで見出しを付けられます。見出し自体は選べません。

<select name="item">
  <optgroup label="文房具">
    <option value="pen">ペン</option>
    <option value="note">ノート</option>
  </optgroup>
  <optgroup label="備品">
    <option value="chair">いす</option>
    <option value="desk">机</option>
  </optgroup>
</select>

複数選択はmultiple

multipleを付けるとリスト形式になり、CtrlキーやShiftキーを押しながら複数を選べます(macOSはCommandキー)。初期値は複数のoptionにselectedを付けます。

<select name="tags" multiple size="3">
  <option value="new" selected>新着</option>
  <option value="sale">セール</option>
  <option value="gift" selected>ギフト</option>
</select>

キー操作に慣れていない人には伝わりにくいので、選択肢が少ないときはcheckboxを並べるほうが選びやすくなります。

checkboxの書き方と初期値

基本の形

checkboxは1個ずつ独立していて、それぞれがオン・オフを持ちます。複数から選んでもらうときは、同じnameでvalueだけ変えて並べます。

<fieldset>
  <legend>オプション(複数可)</legend>
  <label><input type="checkbox" name="option" value="wrap" checked> ラッピング</label>
  <label><input type="checkbox" name="option" value="card"> メッセージカード</label>
  <label><input type="checkbox" name="option" value="bag" checked> 手さげ袋</label>
</fieldset>

初期値はchecked

最初からチェックを入れておきたいものにcheckedを書きます。checked="checked"と書いても同じ意味です。checked="false"と書いてもチェックは入ったままになる点だけ覚えておいてください。属性は「あるか、ないか」だけで判定されるためです。チェックを外しておきたいときは、属性そのものを書きません。

1個だけのcheckbox

「同意する」のような1項目は、checkboxを1個だけ置きます。必須にするにはrequiredを付けます。

<label><input type="checkbox" name="agree" value="yes" required> 利用規約に同意する</label>

radioの書き方と初期値

基本の形

radioは、同じnameを持つもの同士が1つのグループになり、その中で1つだけ選べます。nameが違うと別のグループになり、同時に選べてしまうので、つづりをそろえてください。

<fieldset>
  <legend>色(1つ選ぶ)</legend>
  <label><input type="radio" name="color" value="red"> 赤</label>
  <label><input type="radio" name="color" value="blue" checked> 青</label>
  <label><input type="radio" name="color" value="green"> 緑</label>
</fieldset>

初期値はchecked

初期値にしたい1つにcheckedを書きます。どれにも書かなければ、何も選ばれていない状態で始まります。radioは一度どれかを選ぶと、利用者の操作では「未選択」に戻せません。「選ばない」という答えもありうるなら、「指定なし」という選択肢を1つ用意すると親切です。

同じグループの2つにcheckedを書いた場合、手元のChromium 141ではあとに書いたほうが選ばれました(後述の実行結果のrr)。

入力しやすくする基本:labelとfieldset

labelで文字もクリックできるようにする

checkboxとradioは的が小さいので、labelで文字と結び付けると、文字をクリックしても切り替わるようになります。結び付け方は2通りです。

<!-- 方法1:label で input を包む -->
<label><input type="checkbox" name="agree" value="yes"> 同意する</label>

<!-- 方法2:for と id をそろえる -->
<input type="checkbox" id="agree" name="agree" value="yes">
<label for="agree">同意する</label>

labelがあるときとないときで、文字をクリックした結果を比べました。

labelあり: true / labelなし: false / a.labels.length=1

labelで結び付けたほうだけ、文字のクリックでチェックが入っています。読み上げソフトも、labelの文字を部品の名前として読み上げます。

fieldsetとlegendでグループに名前を付ける

radioやcheckboxのまとまりはfieldsetで囲み、legendに質問文を書きます。「赤・青・緑」だけでは何を聞かれているのかわかりませんが、legendに「色(1つ選ぶ)」と書いておけば、見た目でも読み上げでも質問が伝わります。

工夫 書き方 効果
文字もクリック可能にする labelで包む、またはforとid 押しやすくなる。読み上げにも対応
グループに質問文を付ける fieldsetとlegend 何を選ぶのかが伝わる
まとめて選べなくする fieldsetにdisabled 中の部品が一括で無効になる
必須を伝える requiredと、文字での「必須」表示 ブラウザーの検証と見た目の両方で伝わる
選択肢を縦に並べる CSSでdisplay: blockなど スマートフォンで押しやすい

JavaScriptで値を取得する

部品ごとに取得する

フォームの中の部品は、form.elements.名前で取り出せます。完成形のフォームを開いた直後(初期値のまま)に、次のコードを実行しました。

const f = document.querySelector("#order");
const size = f.elements.size;
const tags = f.elements.tags;

console.log(size.value);                      // 選ばれている value
console.log(size.selectedIndex);              // 何番目か(0始まり)
console.log(size.selectedOptions[0].text);    // 表示テキスト
console.log(tags.value);                      // multiple でも最初の1つだけ
console.log([...tags.selectedOptions].map((o) => o.value));
console.log(f.elements.color.value);          // radio グループの値
console.log(f.querySelector('input[name="color"]:checked').value);
console.log([...f.querySelectorAll('input[name="option"]:checked')].map((c) => c.value));
console.log(f.elements.agree.checked);        // checkbox のオン・オフ
console.log(f.elements.agree.value);          // value 属性の値

実行結果(Chromium 141):

size.value="m"
size.selectedIndex=2
size.selectedOptions[0].text=M
tags.value="new"
tags.selectedOptions=["new","gift"]
elements.color.value="blue"
querySelector(color:checked).value="blue"
option:checked=["wrap","bag"]
agree.checked=false
agree.value="on"

ここから読み取れるポイントは3つです。

  • select multipleのvalueは最初の1つしか返さない。 全部ほしいときはselectedOptionsを使います
  • radioグループはform.elements.名前.valueで選ばれた値が取れる。 同じ名前の部品が複数あるとelementsはRadioNodeListというまとまりを返し、そのvalueが選択中の値になります。何も選ばれていないときは空文字です
  • checkboxのvalueはチェックの有無と関係がない。 agreeは未チェックなのにvalueはonを返しています。オン・オフは必ずcheckedで調べます

取得方法の逆引き表

やりたいこと コード
selectの選択値 select.value
selectの表示テキスト select.selectedOptions[0].text
select multipleの全選択値 [...select.selectedOptions].map((o) => o.value)
radioの選択値(フォーム内) form.elements.color.value
radioの選択値(フォーム外) document.querySelector('input[name="color"]:checked')?.value
checkboxのオン・オフ input.checked
チェック済みcheckboxの値一覧 [...form.querySelectorAll('input[name="option"]:checked')].map((c) => c.value)
チェックされた数 form.querySelectorAll('input[name="option"]:checked').length
フォーム全体をまとめて new FormData(form)

フォームの外にあるradioで何も選ばれていないと、querySelectorはnullを返します。?.valueと書いておくと、エラーで止まらずundefinedになります。

FormDataでまとめて取得する

送信される値がそのまま手に入る

FormDataは、フォームが送信するときの「名前と値の組」をそのまま取り出せる仕組みです。部品ごとの取り方を覚えなくても、送信内容と同じものが手に入るのが利点です。

const fd = new FormData(document.querySelector("#order"));
for (const [name, value] of fd) {
  console.log(name + "=" + value);
}

初期値のままの実行結果:

size=m
tags=new
tags=gift
color=blue
option=wrap
option=bag
mail=0

tagsとoptionが2行ずつ出ています。複数選ばれた項目は、同じ名前で複数の組になります。一方、未チェックのagreeは一覧に出てきません。

複数選択はgetAllで取る

getは最初の1つだけ、getAllは全部を配列で返します。

const fd = new FormData(document.querySelector("#order"));
console.log(fd.get("size"));
console.log(fd.get("tags"));
console.log(fd.getAll("tags"));
console.log(fd.getAll("option"));
console.log(fd.get("color"));
console.log(fd.getAll("mail"));
console.log(fd.get("agree"));
console.log(fd.has("agree"));
get(size)="m"
get(tags)="new"
getAll(tags)=["new","gift"]
getAll(option)=["wrap","bag"]
get(color)="blue"
getAll(mail)=["0"]
get(agree)=null
has(agree)=false

checkboxのグループとselect multipleは、どちらもgetAllで配列として取れます。 1つも選ばれていなければgetAllは空の配列を返すので、そのままlengthやmapを使えます。

Object.fromEntriesは複数の値が1つになる

FormDataを手軽にオブジェクトへ変えるObject.fromEntriesは便利ですが、同じ名前が複数あると最後の1つだけが残ります。

console.log(JSON.stringify(Object.fromEntries(new FormData(form))));
{"size":"m","tags":"gift","color":"blue","option":"bag","mail":"0"}

tagsはnewとgiftの2つを選んでいたのにgiftだけ、optionもbagだけになりました。複数選択を含むフォームでは、次のようにgetAllで補います。

const fd = new FormData(form);
const data = Object.fromEntries(fd);
data.tags = fd.getAll("tags");
data.option = fd.getAll("option");
data.agree = fd.has("agree");

JavaScriptで値を設定する

設定の書き方

やりたいこと コード
selectを選ぶ select.value = "s"
selectを番号で選ぶ select.selectedIndex = 0
select multipleを選び直す for (const o of select.options) o.selected = values.includes(o.value)
radioを選ぶ form.elements.color.value = "red"
checkboxをオンにする input.checked = true
checkboxをすべてオンにする form.querySelectorAll('input[name="option"]').forEach((c) => (c.checked = true))
初期値に戻す form.reset()
const f = document.querySelector("#order");
f.elements.size.value = "s";
f.elements.color.value = "red";
f.elements.agree.checked = false;
for (const o of f.elements.tags.options) {
  o.selected = o.value === "sale";
}

設定後に読み直した結果:

size=s
color=red
agree=false
tags=["sale"]

存在しない値を設定すると未選択になる

select.valueに、どのoptionにもない値を入れると、何も選ばれていない状態になります。

const s = document.querySelector("#size");
s.value = "xl";
console.log(s.value, s.selectedIndex);
value="" selectedIndex=-1

サーバーから受け取った値を初期値として入れるときは、設定したあとにselectedIndexが-1になっていないかを確かめると、選択肢にない値に早く気づけます。

checked属性とcheckedプロパティは別もの

JavaScriptでinput.checked = trueとしても、HTMLのchecked属性は増えません。

const c = document.querySelector('input[name="agree"]');
c.checked = true;
console.log(c.checked, c.defaultChecked, c.hasAttribute("checked"), c.matches(":checked"));
checked=true defaultChecked=false hasAttribute=false matches(:checked)=true
見ているもの 意味 調べ方
checked属性 初期値(リセットで戻る先) input.defaultChecked / hasAttribute("checked")
checkedプロパティ 今の状態 input.checked / CSSの:checked

今の状態を知りたいときはcheckedプロパティか:checkedを使います。 [checked]という属性セレクターは初期値を見ているので、利用者が操作したあとの状態とは一致しません。selectのselected属性とoption.selectedプロパティも同じ関係です。

reset()で初期値に戻る

form.reset()(またはリセットボタン)は、HTMLに書いたselectedとcheckedの状態に戻します。いろいろ操作したあとに実行した結果です。

size=m
tags=new
tags=gift
color=blue
option=wrap
option=bag
mail=0

最初のFormDataとまったく同じ内容に戻りました。

変更イベントはchangeで受け取る

3つともchangeでそろえる

select・checkbox・radioは、利用者が選び直すたびにchangeイベントが発生します。フォームに1つだけリスナーを付ければ、中の部品すべての変更をまとめて受け取れます。

const form = document.querySelector("#order");
form.addEventListener("change", (e) => {
  console.log("change:", e.target.name, "=",
    e.target.type === "checkbox" ? e.target.checked : e.target.value);
});

サイズをLに、色を緑に、同意にチェック、お知らせメールにチェック、メッセージカードの文字をクリック、の順に操作したときの出力です。

change: size = l
change: color = green
change: agree = true
change: mail = true
change: option = true

radioのchangeは、新しく選ばれた側だけで発生します。選択が外れた側(ここでは青)では発生しません。「外れたとき」に処理をしたい場合は、グループ全体のchangeで今の値を読み直します。

inputイベントも3つの部品で発生し、手元の確認ではinput、changeの順でした。

s input
s change
c input
c change
r2 input
r2 change

JavaScriptで値を変えてもchangeは発生しない

前の節でsize.value = "s"などを実行したとき、changeのログは1行も出ませんでした。changeは利用者の操作に対して発生するイベントで、スクリプトからの代入では発生しません。 代入のあとにも同じ処理を走らせたいときは、自分でイベントを送ります。

const s = document.querySelector("#size");
s.value = "m";
s.dispatchEvent(new Event("change", { bubbles: true }));
change: size = m

bubbles: trueを付けると、フォームに付けたリスナーまでイベントが届きます。

連動の例:全選択のcheckbox

「すべて選ぶ」で下の項目をまとめて切り替え、一部だけ選ばれているときは横棒の表示(indeterminate)にする例です。

<label><input type="checkbox" id="all"> すべて選ぶ</label>
<fieldset id="items">
  <legend>備品</legend>
  <label><input type="checkbox" name="item" value="pen"> ペン</label>
  <label><input type="checkbox" name="item" value="note"> ノート</label>
  <label><input type="checkbox" name="item" value="clip"> クリップ</label>
</fieldset>

<script>
const all = document.querySelector("#all");
const items = [...document.querySelectorAll('input[name="item"]')];

all.addEventListener("change", () => {
  items.forEach((c) => (c.checked = all.checked));
});

document.querySelector("#items").addEventListener("change", () => {
  const n = items.filter((c) => c.checked).length;
  all.checked = n === items.length;
  all.indeterminate = n > 0 && n < items.length;
});
</script>

indeterminateは見た目だけの状態で、HTMLの属性では指定できず、JavaScriptからだけ設定できます。送信内容にも影響しません。次の結果のとおり、indeterminateがtrueでもcheckedがfalseなら何も送られません。

checked=false indeterminate=true 送信=[]

送信される値を確かめる

POSTで実際に送ってみる

受け取った内容をそのまま返す小さなサーバーをNode.js 22で立て、完成形のフォームから送信しました。初期値のまま「お知らせメールを受け取る」だけチェックを入れて送った結果です。

POST /echo
size=m&tags=new&tags=gift&color=blue&option=wrap&option=bag&mail=0&mail=1

methodをgetに変え、何も操作せずに送った結果です。

GET /echo?size=m&tags=new&tags=gift&color=blue&option=wrap&option=bag&mail=0

どちらもFormDataで見た内容と同じ並びです。複数選択はtags=new&tags=giftのように同じ名前が繰り返される形で届きます。サーバー側では、同じ名前の値を配列として受け取る書き方が、言語やフレームワークごとに用意されています。なお、PHPのように名前の末尾に[]を付ける(name="option[]")と配列として受け取れる環境もあります。その場合はJavaScript側もfd.getAll("option[]")と、[]を含めた名前で取得します(この[]付きの形は今回は未実行です)。

未チェックのcheckboxは送信されない

どちらの結果にもagreeがありません。チェックの入っていないcheckboxは、名前ごと送信されないのがHTMLの決まりです。「agree=off」のような値が届くわけではありません。

そのため、サーバー側で「届いた項目だけを更新する」という作りにしていると、チェックを外しても保存済みの値がオンのまま残る、ということが起こります。対処は3通りあります。

対処 やり方 向いている場面
hiddenを前に置く 同じnameのhiddenをcheckboxより前に書く JavaScriptなしの通常送信
サーバーで補う 項目が届かなければオフとして扱う サーバー側を変更できるとき
JavaScriptで補う fd.has("agree")でtrue / falseにする fetchで送るとき

hiddenを前に置く方法

完成形のフォームのmailがこの形です。

<input type="hidden" name="mail" value="0">
<label><input type="checkbox" name="mail" value="1"> お知らせメールを受け取る</label>

未チェックならmail=0だけ、チェックありならmail=0&mail=1が届きます。

getAll(mail)=["0","1"] 最後=1

サーバー側で同じ名前の最後の値を採用すれば、未チェックは0、チェックありは1になります。同じ名前が重なったときにどれを採用するかは、サーバー側の言語やフレームワークによって異なるため、使う環境の受け取り方を一度確かめてから取り入れてください。JavaScriptで読むならfd.getAll("mail").at(-1)で最後の値を取れます。

JavaScriptで補う方法

fetchでJSONを送るなら、hasで真偽値に直すのが簡潔です。

form.addEventListener("submit", async (e) => {
  e.preventDefault();
  const fd = new FormData(form);
  const body = {
    size: fd.get("size"),
    tags: fd.getAll("tags"),
    color: fd.get("color"),
    option: fd.getAll("option"),
    agree: fd.has("agree"),
  };
  await fetch("/api/order", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(body),
  });
});

(このfetchの送信先/api/orderは例で、今回は未実行です。)

valueを省略するとonが送られる

agreeにはvalueを書いていません。チェックを入れてFormDataを見ると、次のようになります。

agree=on

checkboxとradioは、valueを省略すると文字列onが値になります。 1個だけのcheckboxなら「届いたらオン」と判定できるので困りませんが、同じnameで複数並べたときにvalueを忘れると、どれを選んでもonになり、区別がつかなくなります。複数並べるときは必ずvalueを書きましょう。

細かい挙動をまとめて確認

送信まわりで迷いやすい形を1つのフォームに詰めて、FormDataの内容を確かめました。

<form id="f">
  <select name="a"><option>りんご</option><option value="b">バナナ</option></select>
  <select name="none" multiple><option value="1">1</option></select>
  <select name="dis">
    <option value="x" disabled selected>選んでください</option>
    <option value="y">Y</option>
  </select>
  <select name="two">
    <option value="1" selected>1</option>
    <option value="2" selected>2</option>
  </select>
  <input type="radio" name="r" value="1"><input type="radio" name="r" value="2">
  <input type="radio" name="rr" value="1" checked><input type="radio" name="rr" value="2" checked>
  <input type="checkbox" name="c1" checked>
  <input type="checkbox" name="c2" value="" checked>
  <input type="checkbox" name="c3" value="yes" checked disabled>
  <input type="checkbox" checked value="noname">
  <input type="radio" name="r2" checked>
</form>
a="りんご"
two="2"
rr="2"
c1="on"
c2=""
r2="on"
r.value=""
has(r)=false
has(none)=false
has(dis)=false dis.value="x"
書き方 結果 覚えておくこと
optionにvalueなし(a) 表示テキスト「りんご」が届く 表示を変えると値も変わる。valueを書くと安定する
select multipleで未選択(none) 送信されない checkboxと同じく名前ごと届かない
disabledのoptionが選択中(dis) 送信されない select.valueはxを返すが、送信内容には入らない
単一selectでselectedが2つ(two) あとのほうが選ばれる selectedは1つにする
radioが未選択(r) 送信されない 必須ならrequiredか初期値を付ける
radioでcheckedが2つ(rr) あとのほうが選ばれる checkedは1つにする
checkboxでvalueなし(c1) on 複数並べるならvalueを書く
checkboxでvalue=""(c2) 空文字が届く 省略と空文字は別の扱い
disabledのcheckbox(c3) 送信されない 見せるだけで値も送りたいならhiddenを併用
nameなし 送信されない nameは必ず書く

必須にするrequiredの挙動

3つの部品にrequiredを付け、未入力のまま検証の状態を調べました。

<form id="f">
  <input type="checkbox" name="agree" required>
  <input type="radio" name="r" value="1" required>
  <input type="radio" name="r" value="2">
  <select name="s" required>
    <option value="">選んでください</option>
    <option value="1">1</option>
  </select>
</form>
for (const e of document.querySelector("#f").elements) {
  console.log(e.name + "(" + e.type + ") valueMissing=" + e.validity.valueMissing
    + " / " + e.validationMessage);
}
agree(checkbox) valueMissing=true / Please check this box if you want to proceed.
r(radio) valueMissing=true / Please select one of these options.
r(radio) valueMissing=true / Please select one of these options.
s(select-one) valueMissing=true / Please select an item in the list.

メッセージが英語なのは、検証に使ったChromiumの表示言語が英語のためで、日本語環境では日本語の文面になります。

部品 requiredの意味 注意点
checkbox その1個にチェックが必要 「複数のうち1つ以上」は表現できない。JavaScriptで数を数える
radio グループのどれか1つが選ばれていればよい 1個に付ければグループ全体に効く(上の結果で2個目もvalueMissing=true)
select 選ばれたoptionの値が空文字でないこと 案内用の先頭optionはvalue=""にする

checkboxのグループで「1つ以上選ぶ」を必須にしたいときは、送信時に数を調べます。

form.addEventListener("submit", (e) => {
  const first = form.querySelector('input[name="option"]');
  const n = form.querySelectorAll('input[name="option"]:checked').length;
  first.setCustomValidity(n === 0 ? "1つ以上選んでください" : "");
  if (!form.reportValidity()) e.preventDefault();
});

(このコードは今回は未実行です。setCustomValidityに空文字以外を渡すと、その部品が検証エラーの扱いになります。)

つまずきやすい点と直し方

症状 原因 直し方
チェックを外しても保存値が変わらない 未チェックのcheckboxは送信されない hiddenを前に置く、またはサーバーやfd.hasで補う
どれを選んでもonが届く valueを書いていない 選択肢ごとにvalueを書く
radioが2つ同時に選べる nameがそろっていない 同じグループは同じnameにする
複数選択なのに1つしか取れない getやselect.value、Object.fromEntriesを使っている getAllやselectedOptionsを使う
何も送信されない nameがない、またはdisabled nameを書く。値も送りたいならhiddenを併用
JavaScriptで値を変えたのに連動しない 代入ではchangeが発生しない dispatchEvent(new Event("change", { bubbles: true }))
初期値を入れたのに空になる selectに存在しない値を代入した selectedIndexが-1でないか確かめる
[checked]で今の状態が取れない 属性は初期値を表す :checkedまたはcheckedプロパティを使う
文字をクリックしても反応しない labelと結び付いていない labelで包む、またはforとidをそろえる
戻るボタンで前の選択が残る ブラウザーが入力を復元している 困る場合はautocomplete="off"を検討(今回は未実行)

よくある質問

Q. selectの初期値を「選んでください」にするには? A. 先頭にvalue=""のoptionを置きます。selectedを書かなければ先頭が初期値になります。selectにrequiredを付けると、案内のまま送信しようとしたときにブラウザーが止めてくれます。

Q. checkboxの値をtrue / falseで受け取りたいときは? A. JavaScriptならinput.checked、FormDataならfd.has("名前")が真偽値を返します。通常の送信では未チェックの項目が届かないので、同じ名前のhiddenを前に置くか、サーバー側で「届かなければオフ」と扱います。

Q. radioの選択を外して未選択に戻せますか? A. 利用者の操作では戻せません。JavaScriptでinput.checked = falseとするか、form.reset()で初期状態に戻します。選ばないことも答えになる場合は、「指定なし」の選択肢を用意すると伝わりやすくなります。

Q. select multipleとcheckboxはどちらを使えばよいですか? A. 送信される形はどちらも同じ(同じ名前の繰り返し)で、getAllで配列として取れます。選択肢が少ないなら、状態がひと目でわかるcheckboxが向いています。

まとめ

  • 初期値はselectがselected、checkboxとradioがchecked。 属性は初期値、プロパティは今の状態を表す
  • 値の取得は、selectとradioがvalue、checkboxがchecked。 フォーム全体ならFormDataが手早い
  • 複数選択はgetAllで取る。 get、select.value、Object.fromEntriesは1つしか返さない
  • 未チェックのcheckboxは名前ごと送信されない。 hiddenを前に置くか、fd.hasやサーバー側で補う
  • valueを省略したcheckboxとradioはonが送られる。 複数並べるときは必ずvalueを書く
  • 変更はchangeで受け取る。 JavaScriptからの代入では発生しないので、必要ならdispatchEventで送る
  • labelとfieldset・legendで、押しやすく伝わりやすいフォームになる

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「HTML select・checkbox・radioの書き方|初期値と値取得」

株式会社RJCでは、HTMLフォームの基本とJavaScriptでの扱い方を仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?