- チェックボックスが「戻る」現象の正体
- checked属性が意味しているもの
- attr()で壊れる理由
- prop()が正しい理由
- defaultCheckedというもう一つの存在
- なぜフォームで問題が顕在化するのか
- ありがちな失敗コード
- なぜjQueryがpropを追加したのか
- checked以外でも同じ問題は起きる
- 覚え方
- 最後に
チェックボックスが「戻る」現象の正体
jQueryでチェックボックスを操作していると、こんな経験が起きます。
- チェックを外したはずなのに送信するとONになっている
- 画面上はOFFなのにJavaScriptではtrueになる
- 再描画やバリデーション後に元に戻る
この原因の多くはバグではありません。checkedは属性ではなく状態だからです。そしてこの違いを無視してattr()を使うと壊れます。
結論として、checkedはHTMLの情報ではなく、ブラウザが持つ実行時の状態です。そのため操作にはprop()が必要になります。
checked属性が意味しているもの
次のHTMLを見ます。
<input type="checkbox" id="mail" checked>
このcheckedは「今チェックされている」という意味ではありません。実は「ページ読み込み時の初期状態」を表しています。
ブラウザはページを読み込むと、この情報を元にDOMオブジェクトを生成します。そして別の場所に「現在状態」を持ちます。
つまりブラウザ内部には2つの値があります。
| 項目 | 意味 |
| checked属性 | 初期状態(defaultChecked) |
| checkedプロパティ | 現在状態 |
ユーザーがクリックすると変わるのはプロパティです。HTMLは書き換わりません。
attr()で壊れる理由
次のコードを書きます。
$('#mail').attr('checked', false);
これがやっているのは「初期状態の定義を外す」ことです。しかし現在のチェック状態は変わりません。すでにDOMが生成されているためです。
つまり
- 画面:変わらないことがある
- JavaScriptの判定:変わらない
- 送信値:変わらない
という不整合が起きます。
特に分かりやすいのが次です。
$('#mail').attr('checked', false); console.log($('#mail').is(':checked'));
trueになる場合があります。なぜならis(':checked')はDOMプロパティを参照しているからです。
prop()が正しい理由
prop()はDOMプロパティを変更します。
$('#mail').prop('checked', false);
これはブラウザが管理している「現在状態」を変更します。
- 表示が変わる
- JavaScript判定が変わる
- 送信値が変わる
すべて一致します。ユーザーがクリックしたときと同じレイヤーを操作しているためです。
defaultCheckedというもう一つの存在
さらにややこしいのがdefaultCheckedです。HTML属性はここに対応します。
console.log(document.getElementById('mail').defaultChecked);
これは初期値です。つまり
- defaultChecked:ページロード時
- checked:現在状態
attr()はdefaultChecked側を触っています。
なぜフォームで問題が顕在化するのか
フォーム送信は現在状態を送信します。つまりcheckedプロパティです。そのためattr()で操作したコードは、画面とサーバの値がズレます。
特に次のケースで顕在化します。
- Ajax送信
- バリデーション後の再描画
- SPA風の画面更新
ユーザーから見ると「勝手に戻る」バグに見えます。
ありがちな失敗コード
if(condition){ $('#mail').attr('checked', true); }
このコードは初期値を変更しているだけです。現在状態を変えたいならpropです。
$('#mail').prop('checked', true);
なぜjQueryがpropを追加したのか
jQuery1.5まではattrが両方を扱っていました。しかしブラウザ差異で不具合が頻発しました。
- IEでは動く
- Firefoxでは動かない
- 逆もある
そのため1.6で分離されました。つまりpropは新機能ではなく、正しいDOMの扱いを明示するAPIです。
checked以外でも同じ問題は起きる
同様の現象は他でも発生します。
- optionのselected
- inputのdisabled
- radioの選択状態
すべて「現在状態」はプロパティです。
覚え方
迷ったら次の基準で判断できます。
- HTMLの意味を変える → attr
- ユーザーの操作状態を変える → prop
チェックボックスは明らかに後者です。
最後に
checkedは見た目の問題ではありません。DOMの状態管理の問題です。jQueryのattrとpropの違いは、ブラウザがHTML文書でありながらアプリケーションでもあることを示しています。
「チェックをつける」という操作は、HTMLを書き換えているのではなく、ブラウザの内部状態を変更しています。propを使うのはjQueryの流儀ではなく、ブラウザの仕様に従っているだけです。