打印

BW9012: Chrome Safari 中空行内非替换元素在某些情况下不占用文档流渲染空间

作者:钱宝坤

标准参考

行内非替换元素的高度由 'line-height' 特性值决定,'margin' 'padding' 'border' 都不加入行内框高度的计算(也不参加线框高度的计算),但是他们还是在行内框周围得到渲染。

具体描述请参考 CSS 2.1 规范的 10.8.1 Leading and half-leading

行内非替换元素的宽度不由 'width' 属性决定,他的实际宽度由其中的内容具体宽度决定,如果为空元素,那么宽度的计算值自然也就是 0。

具体描述请参考 CSS 2.1 规范的 110.2 Content width: the 'width' property

根据标准文档描述可以断定,如果空的行内非替换元素存在 'margin' 'padding' 'border' 值,不管实际宽度是否为 0 ,他都应被渲染出来。

问题描述

当一个空的内联非替换元素前,存在其他内联元素(包裹匿名内联元素)时,如果他们之间仅存在唯一的空文本节点就有可能导致这个空内联非替换元素消失,不在文档流中占据渲染空间。

造成的影响

这个问题会导致布局中某小部分内容消失,如果这部分消失的标记具有交互功能,则这个功能将不可能再被用户使用到。

受影响的浏览器

Chrome Safari  

问题分析

这个问题发生在普通非替换行内元素上,根据 HTML 4.0 规范定义,一般常用的此类元素有: A,ABBR1,ACRONYM,B,BDO,BIG,CITE,CODE,DEL,DFN,EM,FONT,I,INS,KBD,LABEL,Q,S,SAMP,SMALL,SPAN,STRIKE,STRONG,SUB,SUP,TT,U,VAR 等。

【注】:ABBR 标记在 IE6 浏览器中存在脚本处理问题可参考文章 BT9023: IE6 中对 ABBR 元素的相关实现有误 ,本文中将不把此元素列为测试范畴。

以下代码将通过脚本程序构建这些元素的空标记形式,通过样式表让每个元素都拥有了 'border ‘特性,根据规范他应该给被渲染出来。并且每个元空素绑定 click 事件,如果元素不可见,则可以用以此方式检查该元素是否还可以响应用户事件。代码构建了四组示例,第一组中空标记之前的匿名行内非替换元素与空标记之间存在唯一空文本节点;第二组中两个元素间存在多余一个文本节点, 第三组中个元素与空标记紧密相连,第四组在第一组基础上将标记变为非空情况。

<style>
a,acronym,b,bdo,big,cite,code,del,dfn,em,font,i,ins,kbd,label,q,s,samp,small,span,strike,strong,sub,sup,tt,u,var
{font-size:30px; line-height:50px; border:10px solid #0F0; cursor:pointer;}
div.info{font-size:12px; line-height:18px; color:#060; background:#EEE;margin:30px 0 30px 0;}
</style>
<script>
var inlineElements = {
  'a':'<a></a>',
  'acronym':'<acronym></acronym>',
  'b':'<b></b>',
  'bdo':'<bdo></bdo>',
  'big':'<big></big>',
  'cite':'<cite></cite>',
  'code':'<code></code>',
  'del':'<del></del>',
  'dfn':'<dfn></dfn>',
  'em':'<em></em>',
  'font':'<font></font>',
  'i':'<i></i>',
  'ins':'<ins></ins>',
  'kbd':'<kbd></kbd>',
  'label':'<label></label>',
  'q':'<q></q>',
  's':'<s></s>',
  'samp':'<samp></samp>',
  'small':'<small></small>',
  'span':'<span></span>',
  'strike':'<strike></strike>',
  'strong':'<strong></strong>',
  'sub':'<sub></sub>',
  'sup':'<sup></sup>',
  'tt':'<tt></tt>',
  'u':'<u></u>',
  'var':'<var></var>'
}

function getElementSize(element){
  return {w:element.offsetWidth,h:element.offsetHeight};
}

function getStyle(element,styleName){
  return (element.currentStyle)
    ?  element.currentStyle[styleName.replace(/-[a-z]/g, function() {
      return arguments[0].charAt(1).toUpperCase();
    })]
    : (document.defaultView && document.defaultView.getComputedStyle)
      ? document.defaultView.getComputedStyle(element, null).getPropertyValue(styleName)
      : null ;
}

function bindEvent(element,eventName,fn){
  element["on"+eventName] = fn;
}

function buildElementInfo(tagName,element){
  var elementSize = getElementSize(element);
  document.writeln('<div class="info">');
  document.writeln(tagName,' 标记的宽为:',elementSize.w,'px; ','标记的高为:',elementSize.w,'px; ', 'display 值为:', getStyle(element,'display'));
  document.writeln("</div>");
}

function buildElement(title,type,html){
  var elementSize,element;
  document.write(title);
  for (var i in inlineElements) {
    document.writeln('<div>');
    switch(type){
      case 1:
        document.writeln(i);
        document.writeln(" ");
        break;
      case 2:
        document.write(i);
        break;
      case 0:
      case 3:
        document.writeln(i);
        break;
    }
    document.writeln(inlineElements[i]);
    document.writeln("其他文本内容……");
    document.writeln('</div>');
    element = document.getElementsByTagName(i)[type];
    element.innerHTML = html;
    buildElementInfo(i,element);
    bindEvent(element,"click",(function(i){return function(){alert(i+" 标记被点击")}})(i))
  }
}

buildElement('<h2>空行内非替换元素前仅存在唯一空文本节点情况</h2>',0,"");
buildElement('<h2>空行内非替换元素前存在不唯一空文本节点情况</h2>',1,"");
buildElement('<h2>空行内非替换元素与前一行内元素紧密相连渲染情况</h2>',2,"");
buildElement('<h2>有文本内容的行内非替换元素非紧密相连渲染情况</h2>',3,"tag");
</script>

各浏览器运行结果如下:

  IE6 IE7 IE8(Q) IE8(S) Firefox Opera Chrome Safari
空行内非替换元素前仅存在唯一空文本节点情况
样式是否被渲染 1 2
是否占据渲染空间 2
是否可被点击 2
空行内非替换元素前存在不唯一空文本节点情况
样式是否被渲染 1
是否占据渲染空间
是否可被点击
空行内非替换元素与前一行内元素紧密相连渲染情况
样式是否被渲染 2
是否占据渲染空间
是否可被点击
有文本内容的行内非替换元素非紧密相连渲染情况
是否被渲染
是否占据渲染空间
是否可被点击

【注1】:这是 IE6 IE7 IE8(Q) 的空行内元素渲染问题,可以参考文章:RD3029: IE6 IE7 IE8(Q) 的空非替换行内元素渲染方式存在差异。

【注2】: Chrome Safari 中要排除 Q 标记的表现情况,因为这个标记会自动向标记内补充双引号作为文本内容,这破坏了本测试用例对空标记渲染检测的原意。

根据汇总表格可以看出:

  • Chrome Safari 中,空非替换行内元素是否被渲染取决于他与前一个行内元素间是否存在唯一的空文本节点,如果唯一则这个空标记不会被渲染出来。此问题明显是 webkit 渲染引擎实现上的一个 Bug 。
  • IE6 IE7 IE8(Q) 在此处仅是标记的边框没渲染,同时无法通过脚本程序得到具体标记宽高,实际上标记依然占据文档流的渲染空间并可以通过前后两个匿名标记的间隔观察出来,同时这个空标记是可被点击的。这说明 IE6 IE7 IE8(Q) 中的现象与元素是否紧密相连无关。
  • IE8(S) Firefox Opera 在这四组测试中均表现正常。

解决方案

避免使用的空的非替换行内元素,如果必须需要标记内不显示出文本内容,可以使用 'text-indent' 特性将文本负缩进至屏幕外。

参见

知识库

相关问题

测试环境

操作系统版本: Windows 7 Ultimate build 7600
浏览器版本: IE6
IE7
IE8
Firefox 3.6.10
Chrome 7.0.544.0 dev
Safari 5.0.2
Opera 10.62
测试页面: webkit_inline_elment_hidden.html
本文更新时间: 2010-10-09

关键字

webkit Chrome Safari inline non-replace inline element 空文本节点 空非替换行内元素 渲染 消失 隐藏