iframe高度引发探索

背景

最近遇到一个问题,在 div 元素下插入一个 iframe 元素,宽、高、边框都设置为0了,但 div 还是被撑起了一个高度。

 <div style="background: orangered;">
    <iframe src="/" style="width: 0; height: 0;" frameborder="0"></iframe>
 </div>

效果如下:

在这里插入图片描述

分析

inline frame

先从 iframe 元素入手,我们在 HTML4.01 的规范中找到 iframe 相关的定义

The IFRAME element allows authors to insert a frame within a block of text. Inserting an inline frame within a section of text is much like inserting an object via the OBJECT element: they both allow you to insert an HTML document in the middle of another, they may both be aligned with surrounding text, etc.

由此可以看出,iframe(inline frame) 拥有内联元素一样的特性,可以插入文本块,并与周围文本对齐显示。

可替换元素

如果说 iframe 是纯粹的内联元素,但是它又可以设置宽高,这和我们常见的大部分内联元素的特性不符。

原来,从元素本身的特点来讲,元素还分为可替换元素和非替换元素。可替换元素本身往往没有实际的内容,但浏览器会根据据其标签和属性,来决定具体的显示内容。常见的 <img>、<input>、<textarea>、<select>、<video>、<audio> 都可当作可替换元素。

经过上述的组合,我们可以进一步认为 iframe 属于内联可替换元素(inline,replaced element)。关于内联可替换元素的宽和高的计算方式可以查看规范10.3.210.6.2

经过上面的描述,iframe 元素和我们熟知的内联块级元素(inline-block)在显示特性上极为相似。所以我们可以尝试将 iframe 元素换成任意一个设置了 display:inline-block 样式的元素,以此排除 iframe 这个怀疑对象,继续进行分析。果然,替换后,问题依旧存在。

 <div style="background: orangered;">
    <span style="display: inline-block"></span>
 </div>

题外话,那为什么 iframe 的默认显示属性不是 inline-block 呢?

我猜想可能的原因是:inline-block 是 css2.1(2011年) 规范中提出的,而 iframe 元素是 HTML4.01(1999年)规范提出的,所以 inline-block 就没有做为 iframe 的默认显示样式。
但是,从规范看,它们的宽高计算方式都是一样的。

包含块(containing block)

通过上面的转换,既然里面的内联块级元素宽高都为0了,外部元素还是被撑起一个高度。我们就换个角度来看看它的包含块,也就是外层的div元素的高度是怎么计算来的。

由于外层div是属于块级不可替代元素,所以我们在规范 10.6.3 节可以找到答案。

The element’s height is the distance from its top content edge to the first applicable of the following:

  1. the bottom edge of the last line box, if the box establishes a inline formatting context with one or more lines
  2. the bottom edge of the bottom (possibly collapsed) margin of its last in-flow child, if the child’s bottom margin does not collapse with the element’s bottom margin
  3. the bottom border edge of the last in-flow child whose top margin doesn’t collapse with the element’s bottom margin
  4. zero, otherwise

因为块级元素包含非块级元素形成的是内联格式上下文(inline formatting context),所以我们需要按照上面的第一条来计算包含块的高度。即计算 line box 的高度。

line box

关于 line box 的定义和其高度的计算,在规范 9.4.2

An inline formatting context is established by a block container box that contains no block-level boxes. In an inline formatting context, boxes are laid out horizontally, one after the other, beginning at the top of a containing block. Horizontal margins, borders, and padding are respected between these boxes. The boxes may be aligned vertically in different ways: their bottoms or tops may be aligned, or the baselines of text within them may be aligned. The rectangular area that contains the boxes that form a line is called a line box.
The width of a line box is determined by a containing block and the presence of floats. The height of a line box is determined by the rules given in the section on line height calculations.

根据规范的描述,line box的高度跟 line-height 有关,所以我们进一步查看 line-height 的计算方式。

line-height

关于 line-height 的计算,在规范的 10.8 节中。

As described in the section on inline formatting contexts, user agents flow inline-level boxes into a vertical stack of line boxes. The height of a line box is determined as follows:

  1. The height of each inline-level box in the line box is calculated. For replaced elements, inline-block elements, and inline-table elements, this is the height of their margin box; for inline boxes, this is their ‘line-height’. (See “Calculating heights and margins” and the height of inline boxes in “Leading and half-leading”.)
  2. The inline-level boxes are aligned vertically according to their ‘vertical-align’ property. In case they are aligned ‘top’ or ‘bottom’, they must be aligned so as to minimize the line box height. If such boxes are tall enough, there are multiple solutions and CSS 2.2 does not define the position of the line box’s baseline (i.e., the position of the strut, see below).
  3. The line box height is the distance between the uppermost box top and the lowermost box bottom. (This includes the strut, as explained under ‘line-height’ below.)

Empty inline elements generate empty inline boxes, but these boxes still have margins, padding, borders and a line height, and thus influence these calculations just like elements with content.

由于我们的例子中,内部元素没有高度,所以直接按照上面的第三条计算,即计算 strut 的高度。继续往下看 。

strut

何为 strut ?我们还是看看规范。

On a block container element whose content is composed of inline-level elements, ‘line-height’ specifies the minimal height of line boxes within the element. The minimum height consists of a minimum height above the baseline and a minimum depth below it, exactly as if each line box starts with a zero-width inline box with the element’s font and line height properties. We call that imaginary box a “strut.” (The name is inspired by TeX.).

原来每行开始都有一个继承父级元素字体和行高属性的零宽内联框。正是这个假想的内联框,父元素的高度因此而来。

那么到此为止,终于找到了问题的元凶。就是这个假想的 strut

解决方案

找到了问题的根源,我们就可以想办法解决问题了。

  • 外层元素修改方案
    • 让 font-size:0。去掉 strut 高度。
    • display: inline-block 或者 flex。 让无法形成内联格式上下文。
  • 内层修改方案
    • float: left。让内部无法形成 line-boxes 模型,就无 line box,就无高度。
    • display: block 或者 flex。改为块级元素或伸缩布局,使高度属性生效,高度的计算方式就变了。
  • 内外结合修改方案
    • 外层元素设置 line-height:0, 内层元素 vertical-align: top。外层元素设置 line-height:0 时,还会有一点高度。那是因为内部元素 inline-block 默认是基线对齐,即inline-block会下沉到baseline的位置,撑开了父级的高度。

还有更多其他的方案,有兴趣可以试试。

扩展

在例子中,如果把内部元素改为 display:inline 显示,也满足内联格式化上下文和后续的高度计算方式,为什么就没有高度了呢 ?

原来在规范中关于内联格式化上下文还有一段话:

Line boxes are created as needed to hold inline-level content within an inline formatting context. Line boxes that contain no text, no preserved white space, no inline elements with non-zero margins, padding, or borders, and no other in-flow content (such as images, inline blocks or inline tables), and do not end with a preserved newline must be treated as zero-height line boxes for the purposes of determining the positions of any elements inside of them, and must be treated as not existing for any other purpose.

可以看出,没有文字等内容的 line-boxes 模型被看作是零高的模型,视为不存在。所以也就不会影响父级的高度了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章