返回

p 标签的坑

在写 HTML 代码时使用正确的语义化标签是前端工程师的职责,滥用标签可能会带来意想不到的后果。

今天遇到的一个问题就是滥用 p 标签引起的,起因是同事的一个项目遇到一个诡异的 bug,在所有浏览器都正常的情况下唯独 IE9 一直有问题,同事在一直解决不了的情况下找上我帮忙看看。具体的情况是他做了一个联动的 select 选择框,比如选择省之后二级选择框就会将该省下面的市放到这个选择框,代码类似:

<form>
  <div class="province">
    <select name="province" id="province">
      <option value="">省</option>
    </select>
  </div>
  <div class="city" id="city">
    <select name="city">
      <option value="">市</option>
    </select>
  </div>
</form>

为了添加样式每个 select 上都包了一个 div 标签,因为项目是用 drupal 来开发的,所以有相应的模块可用,而此模块是通过 ajax 来加载省市的数据,也因此使用了 drupal 核心的 ajax.js 和 jquery.form.js 来处理。而当一级 select 变化的时候模块的处理方式是局部刷新了页面,可能上面的代码就会变成:

<form>
  <div class="province">
    <select name="province" id="province--2">
      <option value="">省</option>
    </select>
  </div>
  <div class="city" id="city--2">
    <select name="city">
      <option value="">市</option>
    </select>
  </div>
</form>

刷新之后再给每个 select 绑定 change 事件,这种方式实在是很挫。在所有浏览器都正常的情况下 IE9 的表现是第一次选择是正常的,第二次开始就没有反应了。

刚拿到这个问题的时候我也是不停的通过断点来调试,但似乎一切都正常,该加的事件也加上了,甚至在通读了一边 ajax.js 和 jquery.form.js 之后还是毫无头绪,这时我在 ajax.js 中发现这样一段代码:

// If there isn't a form, jQuery.ajax() will be used instead, allowing us to
// bind Ajax to links as well.
if (this.element.form) {
  this.form = $(this.element.form);
}

这里的 this.element 就是对应上面的两个 select,而 this.element.form 返回的就是控件上的 form 标签,而在 IE9 上却得到了 undefined,在其他浏览器中都可以获取正确的 form 标签。在得到事发地点之后就去查看此处 DOM 结构,发现在 chrome 下会看到一些凌乱的 p 标签,再查看源码之后发现了这样的代码:

<p>
<form>
  <div class="province">
    <select name="province" id="province">
      <option value="">省</option>
    </select>
  </div>
  <div class="city" id="city">
    <select name="city">
      <option value="">市</option>
    </select>
  </div>
</form>
</p>

下意识想到如果 p 标签内遇到 block 元素之后渲染会出现问题,而大部分浏览器都能正确的处理,在 IE9 下就混乱了,结果由于这个混乱导致了获取不到正确的 form 元素。在发现问题之后把 p 删除或者换成 div 之后问题解决。

有时候一个顽固的臭虫就是因为一些低级错误引起的。