LoginSignup
4
2

More than 5 years have passed since last update.

「HTMLFormElement をネストさせたらどうなるの ?」っと

Posted at

はじめに

まず、form 要素はネストできません。
http://www.w3.org/TR/html5/forms.html#the-form-element

Content model:
Flow content, but with no form element descendants.
コンテントモデル:
フローコンテントだよ。でも form 要素を子孫に持つことはできないよ。

しかし、世の中にはそんなルールなんてお構いなしで稼働しているサービスもあります。
そのようなサービスを不幸にも修正・改修しなければいけない状況も出てくるでしょう。

この記事では、そんなつらい場面でも冷静に対処できるよう、ネストした form 要素が一体どんな動きをするのかを見てみます。

実験サンプルを用意する

こんなフォームを用意します。
document.forms.foo の中に 3 つのフォームを入れてみました。

test.html
<div id="wtf-form">
  <form name="foo">
    <input type="hidden" name="wtf" value="foo-wtf">
    <form name="bar">
      <input type="hidden" name="wtf" value="bar-wtf">
    </form>
    <form name="buzz">
      <input type="hidden" name="wtf" value="buzz-wtf">
    </form>
    <form name="fuga">
      <input type="hidden" name="wtf" value="fuga-wtf">
    </form>
  </form>
</div>

とりあえず Javascript で調べてみる

さっそく各フォームにアクセスしてみましょう。
ブラウザは Google Chrome v47 を使いました。

typeof document.forms.foo // "object"
typeof document.forms.bar // "undefined"
typeof document.forms.buzz // "object"
typeof document.forms.fuga // "object"

document.forms.bar の霊圧が ... 消えた ...?
DOM がどうなっているのか innerHTML を見てみましょう。

document.getElementById('wtf-form').innerHTML
//  <form name="foo">
//    <input type="hidden" name="wtf" value="foo-wtf">
//    
//      <input type="hidden" name="wtf" value="bar-wtf">
//    </form>
//    <form name="buzz">
//      <input type="hidden" name="wtf" value="buzz-wtf">
//    </form>
//    <form name="fuga">
//      <input type="hidden" name="wtf" value="fuga-wtf">
//    </form>

ネストしていたフォームがバラバラになっています。
よく見ると document.forms.bar がいなくなり、その中にいた input[name="wtf"]document.forms.foo の中にいます。
この状態のフォーム要素の値は取れるのでしょうか。

document.forms.foo.wtf.value // ""
document.forms.bar.wtf.value  // Uncaught TypeError: Cannot read property 'wtf' of undefined
document.forms.buzz.wtf.value // "buzz-wtf"
document.forms.fuga.wtf.value // "fuga-wtf"

はい。
document.forms.buzz.wtfdocument.forms.fuga.wtf は取れますが、document.forms.foo.wtf は空文字列、document.forms.bar.wtf はやはり霊圧存在が消えています。

もっと Javascript で調べてみる

document.forms.foo.wtf.value が空文字列とは一体何なのでしょうか。
もう少し詳しく document.forms.foo の子要素を見てみましょう。

document.forms.foo.elements.length // 2
typeof document.forms.foo.elements.wtf // "object"
document.forms.foo.elements.wtf[0].value // "foo-wtf"
document.forms.foo.elements.wtf[1].value // "bar-wtf"

4 つの input[name="wtf"] があるのを想定したフォームでしたが、ネストがなくなった結果 document.forms.foo に 2 つの input[name="wtf"] が存在する形になってしまいました。

document.forms.foo.elements.wtf が object だとか言っているので添字でアクセスしてみるときちんと値が入っていますね。

このフォームを送信してみると以下のパラメータが飛んでいきます。
?wtf=foo-wtf&wtf=bar-wtf
ふーん。

さて、この document.forms.foo.elements.wtf は何者なのか聞いてみます。

document.forms.foo.elements.wtf.toString() // [object RadioNodeList]
typeof document.forms.foo.elements.wtf.value // "string"

RadioNodeList だそうです。うん。なにそれ。

RadioNodeList とは一体

interface RadioNodeList : NodeList {
  attribute DOMString value;
};

NodeList を継承したインターフェイスですね。
value プロパティを持っています。これが何かというと、

radioNodeList . value [ = value ]
Returns the value of the first checked radio button represented by the object.
オブジェクト中の最初のチェック付きラジオボタンの値を返すよ。

とのこと。
この辺りの仕様を読むと、今回のケースは hidden な要素だから value = "" となってしまうようです。

まとめ

form 要素をネストすると子 form が小要素ごと親に巻き取られてしまう、という知見が得られました。

form 要素のネストはダメ。ゼッタイ。
そんな実装に出会ったら全力で逃げましょう。

番外編 (ここで Internet Explorer ですよ)

稀によくあることですが、今回も IE だけは注意が必要です。
Google Chrome, Firefox, Safari はどれも上記の通りの動作ですが、同じ条件で document.forms.foo.elements.wtf を IE で参照した時に事件は起こります。

document.forms.foo.elements.wtf.toString() // [object HTMLCollection]
typeof document.forms.foo.elements.wtf.value // "undefined"

RadioNodeList かと思いきや HTMLCollection なのです。そして value プロパティを持っていないので undefined になります。
これはマジでやばい。例えば以下の様なケース。

if (document.forms.foo.wtf.value === '') {
  // wtf の値が空の時の処理
}

IE だと value = undefined なので if ブロックをスルーしてしまいますね。
極端な例ですが実際にこんなコードが実在するとかしないとか。

この挙動は IE8, 9, 10, 11, Microsoft Edge どれも同じでした。
素直に

if (!document.forms.foo.wtf.value) {
  // wtf の値が存在しなかったり空の時の処理
}

としましょう。
以上、番外編でした。

4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2