消失的form

今天在写一个前端功能的时候,发现代码里的form标签莫名其妙不见了,可是源码里面是确确实实存在,百思不得其解,后来网上搜索了好久,终于找到了答案,本文简单记录在这里。

现场还原

现在我试着还原现场

<input type="submit" />
<input type="submit" />
<input type="submit" />

大家觉得这样的代码会有问题吗?我当时认为是没有问题的,然而当我在chrome下展示时,发现form2消失了,form1和form3都是正常显示的,在chrome中实际展示的代码如下:

<form id="form1" method="post" action="/?method=form1">
    <div>
        <input type="submit">
    </div>
</form>
<div>
    <form id="form3" method="post" action="/?method=form3">
        <input type="submit">
    </form>
</div>
<input type="submit">

有没有惊呆!!!chrome下的结构和代码完全无法对应嘛?!再来看一个,下面的是原始代码:

<form id="form1" method="post" action="/?method=form1" enctype="multipart/form-data">
    <input type="hidden" name="aa" value="aa" />
    <fieldset>
    <div>
        <form id="form2"  method="post" action="/?method=form2">
            <input type="submit" value="btn2"/>
        </form>
    </div>

    <div>
        <form id="form3"  method="post" action="/?method=form3">
            <input type="submit" value="btn3"/>
        </form>
    </div>
        </fieldset>
    <input type="submit" value="btn1"/>
</form>

放到chrome里面展示是下面的情况:

<form id="form1" method="post" action="/?method=form1" enctype="multipart/form-data">
   <input type="hidden" name="aa" value="aa">
   <fieldset>
    <div>
        <input type="submit" value="btn2">
    </div>
    <div>
        <form id="form3" method="post" action="/?method=form3">
                <input type="submit" value="btn3">
        </form>
    </div>
   </fieldset>
</form>
<input type="submit" value="btn1">

怎么样?!form2不见了!!被chrome吃掉了?!我当时遇到的就是后面这种情况,一开始怀疑是js操作的,后来找啊找,终于确定是浏览器解析的问题。大家不妨再看看代码,试着找找里面的问题。

问题原因

出现这个问题的原因稍嫌复杂,在这篇文章里有详细的解释和实验,最后还给出了解决方案,我这里就简单地讲一下原因和另一种解决方案。

首先,HTML4的标准里面是不允许form下直接嵌套form的,也就是不允许下面这种情况。

<form id="form1">
    <form id="form2">
    </form>
</form>

此时用HTML Validator进行验证,是会报错的。但标准是允许间接嵌套的,也就是开始我举的两个例子。

接下来就是chrome和firefox的bug了(应该算是吧),它们在解析form的间接嵌套时标准很简单,form标签开始<form>后,遇到的第一个结束标签就会认为是一个完整的form,而这中间的<form>会被丢弃掉,其他元素会被自动补全开始结束符,也就是第一种情况。

但如果你使用了<fieldSet>标签,情况看起来会好些,如第二种情况所展示的,form1form2的结束符吃掉,然后把form2结束符</form>之前的元素都包含进去,由于<fieldSet>基本上包含了所有的form数据,所以最终呈现的结果是form2被吃掉了,可是form3是依然存在的。

解决思路

解决办法我想到的两种:

一是继续使用嵌套form,在form1前面加上一个假的form表单,不让它显示就可以了,比如下面这种方法:

<form id="form1">
    <div>
        <form id="fake"></form>
        <form id="form2"></form>
        <form id="form3"></form>
    </div>
</form>

二是不适用嵌套form,用js将相应地表单值读取出来,比如对于下面这种情况:

<form id="form1">
    <div id="div1">
        <input type="text" name="a" />
        <input type="checkbox" name="b" value="1">
    </div>
    <input type="text" name="a2" />
    <input type="checkbox" name="b2" value="1">
</form>

我只想获取div1里面的input数据,此时如果用jquery的话可以用类似下面的代码取到所有的input值。

var params={};
$("#div1 :input").each(function(){
    params[this.name]=$(this).val();
}

这样params里面就存储了所有的input值。

小结

莫名其妙的问题总是会有背后的原因,只要我们不轻易放弃,坚持科学理性的编程态度,总会找到问题根源,将bug解决掉,并收获心底的喜悦,只是有时这喜悦来的太心酸太苦逼太傻逼太莫名其妙了。

参考资料

  1. Netsting form tags in xhtml

  2. Wrap form html

  3. Chrome is eating my form

  4. Overcome nested form

标签: html, form

已有 2 条评论

  1. ld ld

    喜歡你的精益求精的精神.

添加新评论