今日も記憶がhiddenしていて 僕はまた一つ不具合を出す

この記事はだいぶ前に書かれたものなので情報が古いかもしれません
たまには記憶を失くすまで飲むのもあり?

この記事を三行にまとめると

CakePHPでチェックボックスを作成するときのお話
悪いのはIE、悪いのはIE、悪いのはIE……
本当に悪いのはIEではなく、わたくしめです
しょーもない話でありながら、基本的には毎回忘れちゃう自分が悪いんですが、そこをあえてIEさんをディスってお茶を濁しちゃおうという、そんな感じです。こういうとき、ヒール(悪役)をやってくれるIEさんはほんと頼りになりますねっ。IEさんバンザーイ。

まあたいした内容じゃないんで、ヒール、いや、ビールでも飲みながらさらっと読んでもらえばと思います。黒ラベル辺りがオススメです。おつまみにはチーズ鱈など良いんじゃないかと思います。僕もよく食べます。生ハムと同じくらいよく食べます。でも最近スーパーでよく買うのは、生ハムじゃなくて、生ハムっぽい感じの……あれは何だろ? 生ハムみたいにスライスしてあるんだけど、生ハムよりはサラミに近いような……サラミかな? サラミかもしれない。ポークサラミ。それかもしかしt

っと、危ない危ない。しょーもない本題に入る前に、しょーもない余談が延々と続くとこでしたね。さーせん。



labelタグについて

ラジオボタンやチェックボックスにラベルをつけるとき、そのラベルをクリックしてもチェックのON・OFFが切り替えられるようにすることはよくあると思います。

その際、ラベルのつけ方は主に二種類ありますね。ここではチェックボックスの場合を例に挙げてみます。

//パターン1
<input type="checkbox" id="check" />
<label for="check">チェックボックス</label>

//パターン2
<label>
  <input type="checkbox" id="check" />
  チェックボックス
</label>

labelタグにはfor属性ってのがありまして、そこにチェックボックスのIDと同じものを指定すると、そのlabelタグで囲んだところをクリックしたとき、対応するチェックボックスのチェックも切り替わります。これがパターン1。

パターン2の方は、チェックボックスごとlabelタグで囲ってしまうっていうやり方ですね。

どっちの方が良いのかってのはよく分かりませんが、状況に応じて使い分けるか、コーディング規約みたいなのでどっちかに固定しとけば良いと思うんだけど、どうでしょう?



パターン2 + CakePHP

CakePHPのFormヘルパーを使ってチェックボックスを出力する場合、ヘルパーの書き方と実際に出力されるチェックボックスってのは、こんな感じになってるはず。

//ヘルパー
<?php echo $this->Form->checkbox('check', array('id' => 'check')) ?>

//HTML
<input type="hidden" id="check_" name="data[check]" value="0" />
<input type="checkbox" id="check" name="data[check]" value="1" />

Cakeさんはたいそう親切なお方でして、チェックボックスにチェックがついていないときにもPOST時にデータを飛ばそうと、hiddenフィールドを勝手に作ってくれるんですね。普通は、チェックがついてない状態でデータをPOSTすると、$_POST(CakePHPの場合は$this->dataや$this->request->data)の中には何もデータが入ってないんですが、これにより、0という値がPOSTされるようになる。まあ、便利……なのかな?

で、これをさっきのパターン2の感じで書くと、HTMLはこうなります。

<label>
  <?php echo $this->Form->checkbox('check') ?>
  チェックボックス
</label>

//HTML
<label>
  <input type="hidden" id="check_" name="data[check]" value="0" />
  <input type="checkbox" id="check" name="data[check]" value="1" />
  チェックボックス
</label>

これといって問題はないですね。チェックボックスそのものをクリックしても「チェックボックス」という文字をクリックしても、チェックをつけたり消したりできます。



IEの場合

単刀直入に結論を言うと、IEの場合、labelの中にhiddenがいると、「チェックボックス」という文字を押したときに、チェックがつきません。チェックボックスそのものをクリックした場合は問題ないです。

ぶっちゃけた話、普段CakePHPでチェックボックスを作るとき、hiddenを作成してるところまであまり気にしてないから、うっかり忘れちゃうんですよね。過去にも同じミスを何度かしているんですけど、完全にそのこと忘れちゃってるのね。僕の脳内でもhiddenしちゃってんの。

まあ、回避する方法はそんなに難しくなくて、上記のパターン1の書き方にするか、Formヘルパーでチェックボックスを作るときに、hiddenを作らないようにする。あるいは、パターン2のときもサボらずにちゃんとfor属性を書く。

//パターン1
<?php echo $this->Form->checkbox('check', array('id' => 'check')) ?>
<label for="check">チェックボックス</label>

//パターン2-1(hiddenを作らないようにする)
<label>
  <?php echo $this->Form->checkbox('check', array('id' => 'check', 'hiddenField' => false)) ?>
  チェックボックス
</label>

//パターン2-2(for属性あり)
<label for="check">
  <?php echo $this->Form->checkbox('check', array('id' => 'check')) ?>
  チェックボックス
</label>

パターン1は言わずもがなですが、パターン2-1の方は、オプションで「hiddenField」ってやつをfalseにすると、hiddenを作らなくなります。ラジボボタンのときも同じ。for属性をちゃんと書くなら、hiddenはあっても大丈夫。何かパターン2の書き方だと、いちいちfor属性を書かなくてもチェックできるから、つい書くのサボっちゃうのよね。

あるいは、javascriptで制御しちゃうっていう手もありますかね。

<label>
  <?php echo $this->Form->checkbox('check', array('id' => 'check')) ?>
  <span class="check-txt">チェックボックス</span>
</label>

<script>
$('.check-txt').click(function(){
  $(this).prev().trigger('click');
});
</script>

いつものごとくjQueryを使ってる前提での書き方ですけど、文字の方をクリックしたときに、対応するチェックボックスのクリックイベントをトリガーしちゃうって感じですね。これならhiddenがあろうがなかろうが関係ない。

「$(this).parent(‘label’)・・・」のところは、prev()でも良いかもしれませんね。チェックボックスとその次のspanの間に余計なHTMLタグが何もなければだけど。

$('.check-txt').click(function(){
  $(this).prev().trigger('click');
});



実はIEだけじゃなかった

僕はIEで動作チェックをしているときにこの現象が起こって、「ちくしょうまたIEか! いい加減にしやがれ! 天皇陛下バンザーイ!!」って危うくキーボードを壊しかけたんですけど、冷静になって確かめてみたら、IE以外でも同様の現象が確認されました。

ってか、hiddenがlabelの中にいてもチェックできんの、SafariとChromeくらいだった。FirefoxとかOperaもIE側だった。「どうせIE以外は大丈夫なんだよ、オラァ! タピオカパ……ンンン!?」って感じで、チェックできなかった。何度連打してもダメだった。頑張って一秒間に16連打したけどやっぱりダメだった。






そういうわけで、もしチェックボックスごとlabelタグで囲む場合は、手を抜かずにfor属性をつけるなり、中にhiddenが紛れてないかちゃんと確認しましょうっていうお話でした。

別にIEさんは悪くないです。今日のヒールはむしろ僕です。本当は悪くないIEさんをヒールに仕立て上げようとした罰として、ピンヒールで踏まれてしまえば良いと思います。

ま、そういうこともあるさ。こんな日は、記憶がhiddenするくらい瓶ビールでもがぶ飲みすれば良いんじゃないかな。
 もしかしたら何か関連しているかも? 
 質問や感想などお気軽にコメントしてください