読者です 読者をやめる 読者になる 読者になる

Corredor

ウェブ、プログラミングの勉強メモ。

チェックボックスの DOM 要素の取得方法に注意

チェックボックスを取得する JavaScript の書き方が古いと、思わぬバグを引き起こすことがある。

例えば以下のような構成の HTML フォームがあったとして。

<form name="myForm" method="post" action="/Send">
  <p>このページをどこで知りましたか?</p>
  <ul>
    <li><input type="checkbox" name="questionnaire" value="google"> Google 検索で</li>
    <li><input type="checkbox" name="questionnaire" value="web"> 別の Web サイトからのリンクで</li>
    <li><input type="checkbox" name="questionnaire" value="friend"> 友達に聞いた</li>
  </ul>
  <p><input type="submit" value="送信"></p>
</form>

「古い書き方」というのは、名前がよく分からないのだが、Form オブジェクトを捕まえて、name 属性値をドット記法で繋いで書いていくやり方。例えば name="questionnaire" という名前のチェックボックスを取得するときに、以下のように書いたとする。

var checkboxes = document.myForm.questionnaire;
// チェックボックスのチェック状況を1つずつ見て何か処理をする
for(var i =0; i < checkboxes.length; i++) {
  // (何か処理)
}

この書き方をしている場合、HTML によっては問題が起こり得る。

チェックボックスが1つしかない場合

例えば、ユーザによってはチェックボックスが1つしか表示されないような場合があったとする。

<form name="myForm" method="post" action="/Send">
  <p>このページをどこで知りましたか?</p>
  <ul>
    <li><input type="checkbox" name="questionnaire" value="google"> Google 検索で</li>
  </ul>
  <p><input type="submit" value="送信"></p>
</form>

このような HTML に対し、先ほどの JavaScript を発動させると、スクリプトエラーになる。

var checkboxes = document.myForm.questionnaire;
// チェックボックスのチェック状況を1つずつ見て何か処理をする
for(var i =0; i < checkboxes.length; i++) {  /* ← この行でスクリプトエラーになる */
  // (何か処理)
}

なぜかというと、この記法で要素を取得したとき、対象の要素が1つだと、配列ではなく1つの DOM オブジェクトとして取得されるため、checkboxes.length というプロパティが存在しなくなってしまうのだ。

だから、この記法で対象の要素が1つの時にも正しく動作させるには、以下のようにプロパティの存在をチェックしないといけないのだ。

var checkboxes = document.myForm.questionnaire;
// チェックボックスのチェック状況を1つずつ見て何か処理をする
if(checkboxes.length) {
  for(var i =0; i < checkboxes.length; i++) {
    // (何か処理)
  }
}
else {
  // (要素が1つのみの時の処理)
}

そもそもこの記法を止めれば良い

上記のような、name 属性名をドット記法で記述する書き方を何と呼ぶのか知らないが、この書き方は古い書き方のような気がする (最近見ないし)。

こうではなく、ちゃんと getElementsByName() を使ってやれば、要素が1つでも必ず配列で取得できるので、余計な考慮が要らなくなる。

なお、getElementById() を使うには、name 属性ではなく id 属性を振っておかないといけないので、form 要素には id 属性を付けておく。

<!-- id 属性を付与 -->
<form id="myForm" name="myForm" method="post" action="/Send">
  <p>このページをどこで知りましたか?</p>
  <ul>
    <li><input type="checkbox" name="questionnaire" value="google"> Google 検索で</li>
  </ul>
  <p><input type="submit" value="送信"></p>
</form>

チェックボックスの方は id 属性を付けて個別に管理する必要はなく、getElementsByName() で取得してやれば良い。

// こうすれば必ず配列で取れる
var checkboxes = document.getElementById("myForm").getElementsByName("questionnaire");
// チェックボックスのチェック状況を1つずつ見て何か処理をする
for(var i =0; i < checkboxes.length; i++) {
  // (何か処理)
}