css文本模型漫遊指南

前言

在寫做本文章前本來打算只是複習一下 line-heightvertical-height 這兩個屬性而已, 結果發現掉進了一個大坑網上有不少篇文章看的我雲裏霧裏的, 最後決定仍是從頭來一遍吧, 這篇文章是此次的一個記錄.css

此次的故事雖然時由於 line-heightvertical-align 這兩個屬性引發的, 可是實際上本篇文章中主要探討的話題是 "文本是如何渲染", 雖然這兩個屬性與話題關聯很大可是本文中不會過多的說起它們, 請確保熟悉它們.html

內聯元素(行內元素)

你也能夠直接跳過這章, 去閱讀後面精彩的部分, 若是遇到了概念問題能夠在回來進行查閱.ios

咱們這裏不會仔細的討論 "內聯元素", 只是爲了後面的內容作鋪墊, 這些內容可能須要你提早預習.瀏覽器

另外 "內聯元素" 和 "行內元素" 其實是同一個東西只是叫法不一樣, 可是本篇文章中沒有統一叫法.ide

表現

一個內聯元素只佔據它對應標籤的邊框所包含的空間。svg

默認狀況下,行內元素不會以新行開始,而塊級元素會新起一行。工具

常見的內聯元素有:佈局

分類

替換元素

CSS 中, 可替換元素replaced element)的展示效果不是由 CSS 來控制的。這些元素是一種外部對象,它們外觀的渲染,是獨立於 CSS 的。

典型的替換元素有:post

  • <iframe>
  • <video>
  • <embed>
  • <img>

非替換元素

那些沒有 "特異功能" 的 "內聯元素" 就是 "非替換元素", 典型值以下:測試

  • <span>
  • <em>
  • <strong>
  • <i>

佈局

內聯元素除了 "行內元素不會以新行開始" 這種你們都知道的特性外, 還有幾點特殊的表現. 例如塊級元素上的幾個常見屬性:

  • margin
  • padding
  • border

這些屬性對於內聯元素來講不會影響其垂直高度, 可是這不意味着這些內容被裁剪掉了而是由於 margin 是透明的而 padding 一般來講也是透明的.

惟一例外的是 border 它能夠被看到, 不過依然不會影響垂直高度:
圖片描述
圖片中文字 "Emmm" 被 <em> 包裹了起來而且設置了 marginpaddingborder 請仔細觀察在 "垂直方向" 實際上高度並無增長.

注意:這種表現只會出如今 "非替換元素" 上, "替換元素" 的表現是不一樣的後文中會提到.

字體

咱們先從字體開始, 由於文本佈局的基本單位毫無疑問是由文字組成的:

<div>hello world</div>

當討論的文字的時候, 最多見的有關 css 屬性就是 font-size 了, 一般認爲 font-size 指定的值就是文字的大小.

想要證實 font-size 不等於 "字體大小" 實際上很是簡單, 打開瀏覽器的開發者工具你就能夠證實這點, 例如咱們有以下的代碼:

<body>
    <div style="font-size:25px">hello world</div>
</body>

慣性思惟會讓咱們認爲這個元素的大小就是 25px .

哈哈!歡迎來到css世界:
圖片描述
上圖中咱們看到一個 font-size:25px 的元素卻佔據了 32.8px 的高度的空間.

em框

實際上, font-size 屬性與你看到的實際字體大小之間的具體關係由字體的設計者來肯定. 這種關係設置爲字體自己中的一個 em 方框(有人也稱爲 em 框).

css權威指南(第三版)

你設置的 font-size 實際上控制的是文字的 em框 而不是字體的自己的大小, 而 em框 與文本之間的間距是由字體的設計者決定的.

em框 的概念就相似小學使用的田字格, 全部字體都相對於田字格進行書寫.

不過按照上面的理解, font-size 控制的是 em框 的大小, 按照這樣的設計字體大小至少不會超過 "25px" 纔對.

沒錯實際上大部的字體都嚴格遵照了 em框 的大小限制, 當咱們將 line-height 大小設置爲 font-size 同樣的時候:

<div style="font-size:25px;line-height:25px">hello world</div>

咱們發現字體渲染大小都是小於或者等於 "25px" 的:
圖片描述
那麼多出去的高度是從哪裏來的?

實際上字體做者有至關大的權力來控制字體,不只可讓字體大小超過 em框, 還能夠指定字體的上下留白空間.

具體能夠參考這篇文章, 從製做字體的角度解釋了字體的渲染表現, 從而能夠解釋 "不一樣字體之間爲何差別這麼大" 這個使人頭大的問題.

line-height 的默認值

在MDN上對於 line-height 有以下大體的描述:

當 line-height 使用默認值的時候(這個值是 normal), 這個值約爲 1.2(不一樣瀏覽器不一樣), 取決於元素的 font-family.

注意💥:如下的內容是我不嚴謹的猜想, 請不要盲目相信.

不過根據個人測試, 字體設計上提供的超出 em框 的留白是會被 line-height 覆蓋的.

固定 font-size:20px 且無 line-height 狀況下 "微軟雅黑" 字體行框高度爲 26.4px, 一旦手動添加 line-height:1.2 後行框高度變爲 24px.

例子地址:

https://jsbin.com/dicaqocize/...

baseline

"baseline" 被稱爲 "基線", 也是由字體做者定義的, 不過常見的文字基線是該字體的英文字母小寫 "x" 的底邊緣, 若是說 em框 是田字格, 那麼 baseline 就是英語練習冊上的 "四線三格", 指定了文字對齊的位置, 有以下的例子:

<body>
  <div style="font-size:25px">
    <span style="font-family:Ubuntu Mono">xYz你好</span>
    <span style="font-family:微軟雅黑">xYz你好</span>
    <span style="font-family:宋體">xYz你好</span>
  </div>
</body>

圖片描述
咱們指定了多種不一樣的字體, 因爲這幾種字體的基線都是 "小寫英文字母x的底邊緣", 即便他們的默認行高不一樣可是經過基線對齊後它們在視覺上造成了統一, 下面的這張圖片能夠揭示它們對齊的方式, 圖片中綠色的線表明基線可能的位置:
圖片描述
可是一旦選中這些文字即可以察覺它們之間的差別:
圖片描述

行佈局

🔥🔥🔥💥💥前方大量術語警告.

行佈局實際上要比常提到的塊佈局複雜的多. 因此在瞭解行佈局前咱們須要瞭解幾噸相關的術語, 固然若是你已經瞭解過了能夠直接跳過.

  • 行內元素(inline element)
    一個行內元素只佔據它對應標籤的邊框所包含的空間, 默認狀況下, 行內元素不會以新行開始, 而塊級元素會新起一行.
    也就是常見的 <span> <a> <em> ... 等這些 display:inline 的元素.
  • 匿名文本
    <p>hello world</p> 中的 "hello world" 沒有被 "行內元素" 包裹起來, 因此它就是 "匿名文本".
    注意:不要忘記任何存在於 HTML 中的換行和空格這些實際上都是 "匿名文本".
  • 替換元素(replaced element)
    內容展現不受 css 控制的元素被稱爲 "替換元素", 例如 <img> <video> <audio> <iframe> ....
    這些元素都有一個特色, 雖然你能夠控制其盒模型屬性,例如: width height... 可是你沒法操控其內部的渲染,例如: 你沒法控制 <iframe> 內部顯示頁面的具體細節.
  • 內容區
    多個連起來的em框 組成內容區.
    對於 "替換元素" 來講, 元素的固有高度在加上可能存在的外邊距邊框或者內邊距.
  • 行間距

    line-height - font-size 的差就是行間距(參考 em框 小節).
    通常來講行間距能夠被平均分爲兩部分, 一部分在行前一部分在行後.
    能夠理解爲 word 中的 "段前" "段後"的概念, 不過在 css 中只有 "行間距" 沒法單獨設置 "行前" 和 "行後".

  • 行內框
    含有行間距的外邊緣就是行內框的大小. 對於 "非替換元素" 來講 line-height 大小就是 "行內框" 的高度.
    不過並不是只有 "非替換元素" 能夠擁有行內框, 當元素是替換元素的時候, 行內框的高度等同於 "替換元素" 內容區高度, 由於行間距不會應用到替換元素上.
  • 行框

    行框是由一行中最高的行內框和最低的行內框所撐開的.

至關很差理解對吧, 那麼爲何有這麼多概念呢? 考慮下方的圖片:
圖片描述
在一行文本中可能會存在各類因素影響着文本的顯示, 例如上圖中存在着多種字體的狀況. 並且這張圖片中尚未考慮同一行存在 <img> 這種替換元素的狀況.

複雜的設計其實是爲了知足複雜的須要.

創建框

爲了方便考慮, 咱們如今只討論一行或者多行中只存在 "非替換元素" 和 "匿名文本" 的狀況.

咱們知道 "font-size 肯定了內容區域的高度(由於 font-size 肯定了 em框 而多個 em框 構成內容區域).

若是一個行內元素的 font-size 是 15px, 那麼內容區域的高度就是 15px, 且 em框 的高度也是 15px:

<span style="font-size:15px">hello world</span>

假設咱們設置 line-height 爲 21px, 那麼用戶代理(瀏覽器)會經過 line-height 減去 font-size 而後在除以2, 將這兩份高度補到到 em框 的上面以及下面此時構成了 "行內框":
圖片描述

此時咱們在段落中填入一個超大號的 <strong>:

<p style="font-size:25px;line-height:25px">
    hello world<strong style="font-size:80px">Emmmm.</strong>
  </p>

這個例子中 "hello world" 這個匿名文本的 line-height 是 25px(line-height有繼承特性), 一樣的 <strong> 標籤中的內容也繼承了 line-height 也就是 25px.

根據 "行內框" 計算規則 line-height 減去 font-size 除以 2 --> (25-80)/2 = -27.5 這些負值被添加到原有的 em框 上構成 "行內框".

因此就形成了一種 "行內框" 比 "內容區域" 小的狀況, 並且更加有趣的是 "行內框" 的下邊緣實際上會向上提高, 由於大部分的文字都靠近 em框 的中下部分:
圖片描述
在默認狀況下文本是按照 "基線對齊" 的, 因此會形成下面的結果, 行框高度超出了 line-height 的高度:
圖片描述
計算完成 "行內框" 後的 <strong> 標籤的基線掉出了 "行內框", 因爲文本須要按照 "基線對齊" 因此 <strong> 和 "hello world" 文本進行基線對齊, 不過計算完成後的 <strong> 的 "行內框" 被總體上擡甚至比基線還要高, 而 "行框" 是根據整行文本中 "行內框" 的最高值和最低值決定的, "hello world" 文本佔據了最低值, 而最高值由 <strong> 標籤佔據, 因此 "行框" 的高度比 line-height 還要高.

下面的這張圖描述了這種渲染行爲:
圖片描述

vertical-height 實戰

CSS 的屬性 vertical-align 用來指定行內元素(inline)或表格單元格(table-cell)元素的垂直對齊方式。

vertical-height 是一個十分複雜的屬性, 其中有幾個屬性的做用方式是和以前討論的 "行框模型" 的關係是十分密切的, 包括如下幾個屬性:

  • top

    行內框文本頂端與行框的頂端進行對齊

  • bottom

    行內框的底部與行框的底部進行對齊

  • text-top

    將元素行內框的頂端與父元素的內容區的頂端對齊

  • text-bottom

    將元素行內框的底端與父元素的內容區的底端對齊

  • middle

    將元素行內框的垂直中點與父元素基線上的 0.5ex 處對齊

  • super

    將元素的內容區和行內框上移. 上移動的距離由用戶代理實現.

  • sub

    將元素的內容區和行內框下移. 下移動的距離由用戶代理實現.

  • 百分比

    相對於行高計算後在將值應用到基線上控制上下移動的距離.

  • 其餘值

    px em .... 等常見的 css 單位

接下來咱們來使用 "top" 這個屬性來研究一番, 咱們在以前的例子中新增一些內容:

<p style="font-size:25px;line-height:25px">
    hello world
    <strong style="font-size:80px">Emmmm.</strong>
    <span style="vertical-align:top">foobar</span>
  </p>

你是否能夠解釋下方的渲染結果呢:
圖片描述

咱們新添加的 "內聯元素" <span> 繼承了父元素的 font-size:25px;line-height:25px, 因爲 <span> 標籤設置了 vertical-align:top 因此他的 "行內框" 頂部和咱們的 "行框"(藍色區域) 頂部進行了對齊.

解析圖:
圖片描述

  • 亮綠色 - 行框
  • 紅色透明區域 - 行內框區域
  • 青色 - 基線
  • 藍色 - 行內框與行框對齊高亮

咱們再在原有的基礎上加點料:

<p style="font-size:25px;line-height:25px">
    hello world
    <strong style="font-size:80px">Emmmm.</strong>
    <span style="font-size:40px;vertical-align:top;line-height:10px">foobar</span>
  </p>

相信你能夠解釋下方的結果了吧:
圖片描述
由於裝載 "foobar" 文本的 <span> 標籤, 被設置了一個較大的字體和一個極小的行高, 文本每每在 em框 靠下的位置而通過 "行內框" 生成後, 上下各自減去一樣大小的數值, 致使計算完成的 "行內框" 的底部提高, 甚至超過了文字的基線.

使用 vertical-align:top<span> 的行內框的頂部與行框進行對齊, 致使部分超出 <span> 元素的 "行內框" 的文本超出 "行框".

line-height 實戰

在以前的章節中咱們知道了 line-height 的高度實際上就是 "行內框" 的高度. 若是設置了錯誤的 line-height 的高度可能會帶來多行疊加的狀況.

當一行中含有不一樣大小字體的時候咱們該如何指定一個良好的 line-height 的呢, 例如:

<p >Lorem ipsum dolor sit amet, consectetur adipisicing elit.
    <strong style="font-size:50px;">big Text</strong>
    dolorum inventore tempora, laboriosam esse a veritatis 
    quae nihil mollitia
  </p>

渲染的結果:
圖片描述
問題所在的緣由就是 "行內框" 的高度比 "內容區域" 小, 致使多出去的內容區域渲染的內容溢出. 解決的問題很簡單確保行內框要比內容區域大就能夠了.

咱們手動的給大號字體指定一個 line-height 其值要大於等於 font-size:

<p >Lorem ipsum dolor sit amet, consectetur adipisicing elit.
    <strong style="font-size:50px;line-height:1em">big Text</strong>
    dolorum inventore tempora, laboriosam esse a veritatis 
    quae nihil mollitia
  </p>

或者使用 line-height 的縮放因子:

<p style="line-height:1.5">Lorem ipsum dolor sit amet, consectetur adipisicing elit.
    <strong style="font-size:50px">big Text</strong>
    dolorum inventore tempora, laboriosam esse a veritatis 
    quae nihil mollitia
  </p>

因爲 line-height 具備繼承的特性因此 <p> 下的全部內容都繼承了這個值, 而 "縮放因子" 是根據當前元素的 font-size 進行計算的, 因此 <strong> 會有一個相對對其字號 1.5 倍大小的 line-height 值.

留意 border

不過這裏依然存在一些問題, 咱們知道 "行內元素" 能夠設置 padding marginborder, 其中 marginpadding 在垂直方向的值是能夠設置也存在可是不會影響到垂直方向的表現.

bordermargin 還有 padding 的表現一致, 可是有一點不一樣的是 border 大部分狀況下都是由顏色的. 若是 border 在垂直方向的高度大於 "行間距" 的話, 這樣又會形成重疊:

圖片描述

所以在指定 line-height 的時候你還須要囊括 boder 的高度這樣才能避免重疊的問題:

<p style="line-height:1.5">Lorem ipsum dolor sit amet, consectetur adipisicing elit.
    <strong style="font-size:50px;border-top:20px solid;line-height:calc(1.5em + 20px)">big Text</strong>
    dolorum inventore tempora, laboriosam esse a veritatis 
    quae nihil mollitia
  </p>

使用 calc 能夠進行自動計算, 固然手動指定一個高度也是能夠的.

替換元素

在此以前咱們只討論了 "非替換元素" 的表現, "替換元素" 表現有些區別因此這些進行詳細介紹.

不過在此以前, 咱們再次回顧一下 "替換元素" 有哪些呢, 常見的有:

  • <iframe>
  • <video>
  • <embed>
  • <img>

以及 "創建框" 中和 "非替換元素" 稍有不一樣的兩個步驟:

  • 內容區

    對於 "替換元素" 來講, 元素的固有高度在加上可能存在的外邊距邊框或者內邊距.

  • 行內框

    當元素是替換元素的時候, 行內框的高度等同於 "替換元素" 內容區高度, 由於行間距不會應用到替換元素上.

如今開始觀察一個實際的例子而且嘗試解釋佈局的緣由:

<body>
  <p style="font-size:20px;line-height:1">hello <img src="http://temp.im/100x30" alt=""> world</p>
</body>

圖片:渲染結果:
圖片描述
根據以前的 "行內框" 的描述, 由於咱們的 <img> 內容的高度是 30px, 這個高度對於 "替換元素" 來講既是 "內容區" 的高度也是 "行內框" 的高度, 因此這二者的高度都是 30px.

默認狀況下 vertical-align 的對齊方式是 "baseline"(基線) 對齊, 對於 "替換元素" 來講 "基線" 就是元素的底邊緣, 因此 <img> 會和左右兩側的文字進行對齊, 所以咱們能夠看到 <img> 元素下方有留出空白, 其實是由於對齊而被提高了.

正由於 "基線" 對齊致使 <img> 的行內框被提高致使 "行框" 變大才從而間接的致使 "行框" 的高度增長變爲了 32.4px 而不是圖片的高度 30px, 以下圖所示:
圖片描述

  • 紅色區域 - 文字行內框區域
  • 藍色區域 - 圖片行內框區域
  • 綠色線 - 基線
  • 黑色框 - 行框

"替換元素" 影響了行框的高度可是並非經過修改父元素的 line-height 的完成的, 或者修改自身的 line-height 來完成的.

<img> 這個 "替換元素" 與其餘 "非替換元素" 沒有什麼不一樣, 它也會繼承 line-height 也能夠設置 line-height, 可是 line-height 不會影響到它們(svg會受到影響), 例如咱們給 <img> 設置一個 100px 的 line-height 可是這不會影響到他的 "行內框" 的大小:

<body>
      <p style="font-size:20px;line-height:1">hello <img src="http://temp.im/100x30" style="line-height:100px;"> world</p>
  </body>

修改後的高度依然和以前的例子高度一致.

不過 line-height 的值能夠被 vertical-align 借用, 這個屬性的百分比單位是基於 line-height 進行計算的, 根據計算的結果在當前的基線上進行上下移動:

img{
  line-height:100px;
  vertical-align:50%;
}

這會令 <img> 元素相對於基線向上移動 50px.

對了, 不要忘記 "替換元素" 的內容區域是包括了 margin paddingborder 的, 這點和 "非替換元素" 是有區別的:
圖片描述
看到了嗎垂直方向的高度增長了, 仔細觀察你能夠看到 margin-top 和行框(藍色區域)之間存在着一小段距離.

inline-block

當用戶給某個元素的 display 設置爲 inline-block 屬性的時候他就創造出了一種介於 "內聯元素" 和 "塊級元素" 的混合產物. 這種 "inline-block" 的做用細節實際上和身爲 "替換元素" 例如: <img> 標籤十分類似:

它就像圖像同樣放在一個文本中, 實際上, 行內塊元素會做爲替換元素放在行中. 這說明, 行內塊元素的底端默認地位於文本行的基線上, 並且內部沒有分隔符.

在行內塊元素內部, 會像塊級元素同樣設置內容的格式. 就像全部塊級或行內替換元素同樣, 行內塊元素也有屬性 widthheight, 若是比周圍內容高, 這些屬性會使行高增長.

-- css 權威指南(第三版)

例以下面的代碼咱們給 <span> 設置了 display:inline-block 屬性:

<style>
    #test{
      display:inline-block;
      border:2px solid green;
      width:6em;
      margin:5px;
      padding:5px;
      color:blue;
    }
  </style>
  <div>
    helloworld helloworld helloworld <p id="test">emmmmmmm</p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Adipisci, iure, magnam! Quam autem tempora quidem ut. Doloremque luptatibus. Eaque!
  </div>

這段代碼的結果是:
圖片描述
能夠看到被標記爲 inline-block 的元素的渲染和 "替換元素" 很是相似, 只有一個不一樣就是垂直對齊方式是根據內部文原本進行對齊的, 而不是 "替換元素" 的外邊距的底邊緣.

由於表現和 "替換元素" 同樣當一行中的空間不夠的時候他不會像 "行內框" 那樣行尾斷開進行換行, 而是跑到下一行來獲取空間:
圖片描述
不過請注意inline-block 垂直對齊根據內部文本基線進行對齊能夠會形成意想不到的狀況:

<body>
  hello world
  <div style="display:inline-block;width:100px;height:100px;word-wrap:break-word;
  background-color:red;color:#fff;">
      <span>
          HelloWorld HelloWorld Helloworld
      </span>
  </div>
  <span >Hello World</span>
</body>

渲染的結果:
圖片描述

對齊是根據 inline-block 框中的第三個 "hello world" 文字的基線進行對齊的.

引用&參考

em框

https://stackoverflow.com/que...
https://en.wikipedia.org/wiki...
http://www.d.umn.edu/~lcarlso...
https://juejin.im/post/59c9bc...

匿名盒子

https://maxdesign.com.au/arti...
https://stackoverflow.com/que...
https://blog.csdn.net/ixygj19...
https://www.cnblogs.com/chaog...

line-height

https://developer.mozilla.org...

vertical-align

https://developer.mozilla.org...

行內元素(內聯元素)

https://developer.mozilla.org...

其餘

css 權威指南(第三版)
張鑫旭老師的免費視頻課程
相關文章
相關標籤/搜索