前言

        在工作中遇到过设置行高不生效的问题,仔细研究后才知道自己对行高的理解远远不够,因此特地在这里总结一下。看一个比较简单的例子:

<!--hexoPostRenderEscape:<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">&lt;style&gt;</span><br><span class="line">    .parent &#123;</span><br><span class="line">        line-height: 40px;</span><br><span class="line">    &#125;</span><br><span class="line">    .child &#123;</span><br><span class="line">        line-height: 20px;</span><br><span class="line">        background-color: red;</span><br><span class="line">    &#125;</span><br><span class="line">&lt;/style&gt;</span><br><span class="line">&lt;div <span class="class"><span class="keyword">class</span></span>=<span class="string">&quot;parent&quot;</span>&gt;</span><br><span class="line">    &lt;span <span class="class"><span class="keyword">class</span></span>=<span class="string">&quot;child&quot;</span>&gt;我的行高是20px我的行高是20px我的行高是20px我的行高是20px我的行高是20px我的行高是20px我的行高是20px我的行高是20px&lt;/span&gt;</span><br><span class="line">    &lt;p <span class="class"><span class="keyword">class</span></span>=<span class="string">&quot;child&quot;</span>&gt;---我的行高是20px我的行高是20px我的行高是20px我的行高是20px我的行高是20px我的行高是20px我的行高是20px我的行高是20px---&lt;/p&gt;</span><br><span class="line">&lt;/div&gt;</span><br></pre></td></tr></table></figure>:hexoPostRenderEscape-->

        在上面的一段代码中,父级div的line-height是40px,子级span和p的line-height为20px。按照我之前的理解,line-height就是设置字体行高的,设置什么是什么就是了,这种明确指定像素的情况,不就是20px嘛。好吧,看看结果:

简单的例子的结果

        yeah~,不是我认为的结果,p标签和span标签显示的行高完全是不同的。还是先熟悉下相关的概念吧。

要熟悉的概念

内联盒模型

        一说到盒模型,我们都会立马想到 margin、padding、content、border、box-sizing等,但这个只是 CSS 中盒模型的块级盒模型(block box)。我们今天要说的是另外一个被我们忽略掉的内联盒模型(inline box)。内联盒指的是盒子内部的构建模型,作用上关键是内容区域(conetnt)。

内联盒模型

  • 内联盒子(inline box)分为内联盒子和匿名内联盒子。内联盒子不会让内容成块显示,而是排成一行。
  • 行框盒子(line box)每一行就是一个行框盒子,由许多内联盒子(inline box)组成。
  • 幽灵空白节点 (struct),每个行框盒子之前都有一个。

幽灵空白节点

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”.

        上面是规范中对幽灵空白节点的定义:每个行框盒子(line box)的前面都有一个0宽度的内联盒子(inline box),这个内联盒子有字体和行高的属性,被称做struct。因为这个内联盒子在页面上看不到,也无法通过脚本获取,但却真实存在,有点像文字节点一样(with the element’s font and line height properties)影响着页面的渲染,因此这个struct被我们成为“幽灵空白节点”。

        需要注意的是,幽灵空白节点的出现的前提是文档声明必须是 HTML5 文档声明(),如果是 HTML5 之前的文档声明,幽灵空白节点是不存在的。

        我们先来看下幽灵空白节点吧。

<!--hexoPostRenderEscape:<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">&lt;style&gt;</span><br><span class="line">    .parent &#123;</span><br><span class="line">        background-color: lightgrey;</span><br><span class="line">    &#125;</span><br><span class="line">    .child1 &#123;</span><br><span class="line">        display: inline-block;</span><br><span class="line">    &#125;</span><br><span class="line">&lt;/style&gt;</span><br><span class="line">&lt;div <span class="class"><span class="keyword">class</span></span>=<span class="string">&quot;parent&quot;</span>&gt;</span><br><span class="line">    &lt;span <span class="class"><span class="keyword">class</span></span>=<span class="string">&quot;child1&quot;</span>&gt;&lt;/span&gt;</span><br><span class="line">&lt;/div&gt;</span><br><span class="line">&lt;div <span class="class"><span class="keyword">class</span></span>=<span class="string">&quot;parent&quot;</span>&gt;</span><br><span class="line">    &lt;p <span class="class"><span class="keyword">class</span></span>=<span class="string">&quot;child2&quot;</span>&gt;&lt;/p&gt;</span><br><span class="line">&lt;/div&gt;</span><br></pre></td></tr></table></figure>:hexoPostRenderEscape-->

        结果见下图。可以看到,父级div没有设置高度,span标签里没有文字,但最终的渲染结果是父级是有高度的,这个高度就是幽灵空白节点造成的。

幽灵空白节点

眼尖的你可能会提出质疑:既然每个行框盒子之前都有一个空白幽灵节点,为什么要设置span标签为inline-blcok。嗯,这里再补充一段规范。

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 box里没有文字、空格、非0的margin或padding或border的inline元素、或其他in-flow内容(比如图片、inline-block 或 inline-table元素),且不以保留的换行符结束的话,就会被视作高度为0的line box。换言之,如果只有一个空的span标签,是不会有空白幽灵节点出现的。

结论与理解

        先抛出网上大佬们给出的结论:

  • 行内元素的line-height属性是去设置该元素所在行框盒子(line box)的行高,行框盒子取其内部所有内联盒子的行高的最大值,定为当前行的行高
  • 换行后生成新的行框盒子,新生成的行的行高,重新在当前行包含的内联盒子的行高中取最大值

        按照上面的结论,我们先画出内联盒模型:

内联盒模型

  • 先解释span标签包裹的文字为什么行高是40px。由上文我们可以知道,幽灵空白节点和文本节点一样,有行高的属性,因为自身没有设置line-height, 因此继承了父级 div 的行高 40px。由span标签组成的inline box 的高度则由自身的行高决定,为20px。 这样,第一行的行框盒子line box的行高取struct和inline box的最大值,就是40px。如果要使span标签上的line-height 生效,那么按照最大值的原则,必须要给span大于40的行高才行。由于换行产生了新的行框盒子(line box),第二行和第三行的内联盒模型和第一行是一致的,行高同样为40px。
  • 至于p标签包裹的文字为什么行高是20px,就很好理解了,因为没有幽灵空白节点呗。

        我们再看一个稍微复杂那么一点点的情况:

<!--hexoPostRenderEscape:<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">&lt;style&gt;</span><br><span class="line">    .parent &#123;</span><br><span class="line">    line-height: <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line">.child1 &#123;</span><br><span class="line">    line-height: 20px;</span><br><span class="line">    background-color: red;</span><br><span class="line">&#125;</span><br><span class="line">.child2 &#123;</span><br><span class="line">    line-height: 40px;</span><br><span class="line">    background-color: green;</span><br><span class="line">&#125;</span><br><span class="line">.child3 &#123;</span><br><span class="line">    line-height: 80px;</span><br><span class="line">    background-color: blue;</span><br><span class="line">&#125;</span><br><span class="line">&lt;/style&gt;</span><br><span class="line">&lt;p <span class="class"><span class="keyword">class</span></span>=<span class="string">&quot;parent&quot;</span>&gt;</span><br><span class="line">    &lt;span <span class="class"><span class="keyword">class</span></span>=<span class="string">&quot;child1&quot;</span>&gt;我的行高是20px我的行高是20px我的行高是20px我的行高是20px我的行高是20px我的行高是20px&lt;/span&gt;   </span><br><span class="line">    &lt;em <span class="class"><span class="keyword">class</span></span>=<span class="string">&quot;child2&quot;</span>&gt;我的行高是40px我的行高是40px我的行高是40px我的行高是40px我的行高是40px我的行高是40px&lt;/em&gt;   </span><br><span class="line">    &lt;b <span class="class"><span class="keyword">class</span></span>=<span class="string">&quot;child3&quot;</span>&gt;我的行高是80px我的行高是80px我的行高是80px我的行高是80px我的行高是80px我的行高是80px&lt;/b&gt;</span><br><span class="line">&lt;/p&gt;</span><br></pre></td></tr></table></figure>:hexoPostRenderEscape-->

        结果见下图,如果你能清楚的解释原因,就说明我讲明白了。

练习

        (完结)