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 模型被看作是零高的模型,視爲不存在。所以也就不會影響父級的高度了。

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