5

带你探索神秘的“幽灵空白节点”

 3 years ago
source link: https://zhuanlan.zhihu.com/p/391118319
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

带你探索神秘的“幽灵空白节点”

关注微信公众号:大转转FE。 一个有趣的前端团队~

在日常开发编写 CSS 代码的时候,时常会遇到一些奇怪的现象。比如以下几个场景

v2-4dd8e6d07922ab4a46da5ce47be1b58a_720w.jpg图1v2-b3ef2367887c014b0c785e90cce32778_720w.jpg图2

图 1 中,图片下面为什么会出现一段空白高度? 在我们正常的认知中,块级元素的height在不设置具体值的时候,默认为0,高度靠内容撑开。图 2 中<div>标签的22px的高度是哪里来的?这究竟是 html 的标签的扭曲,还是 css 属性的沦丧?接下来就让我们一起去揭开这“幽灵空白节点”背后的秘密。

前情提要

首先我们需要了解一个概念,叫做基线,在所有内联相关模型中,凡是涉及到垂直方向的排版或者对齐的,都离不开最最基本的基线(base-line),那么基线是怎么定义的呢?在维基百科中有这么一个示意图:

v2-34638a43409623a327ee2174d281b035_720w.jpg图3

根据图片中标示,字母 x 的下边缘(线)就是基线。

在 CSS 中有两个重要属性,line-heightvertical-align都与基线有关,line-height行高的定义就是两个基线之间的距离,vertical-align的默认值就baseline,也就是基线对齐。对于内联元素,我们要认识到,虽然vertical-alignline-height看不见,但实际上到处都是

那么标签元素的基线如何确认呢?在 CSS2 的可视化格式模型文档中有这样一段话:

The baseline of an ‘inline-block’ is the baseline of its last line box in the normal flow, unless it has either no in-flow line boxes or if its ‘overflow’ property has a computed value other than ‘visible’, in which case the baseline is the bottom margin edge.”

也就是说:一个inline-block元素,如果里面没有inline内联元素,或者overflow不是visible,则该元素的基线就是其margin底边缘,否则,其基线就是元素里面最后一行内联元素的基线。

其次我们还需要理解一个概念叫做line boxes模型,下面是一段简单的代码:

 <p>这是一行普通的文字,这里有个 <em>em</em> 标签。</p>

在这段代码中涉及到 4 个boxes

  • 1、首先是<p>标签所在的containing block(包含块)
  • 2、然后是inline box(内联盒子),如下图标注,inline boxes不会让内容成块显示,而是排成一行
图4
  • 3、line box(行框盒子),在containing boxes里面,一个一个的inline boxes组成了line boxes
  • 4、content area(内容区域),内容区域指一种围绕文字看不见的盒子,其大小仅受字符本身特性控制,本质上是一个字符盒子(character box),但是有些元素,如图片这样的替换元素,其内容显然不是文字,不存在字符盒子之类的,因此,对于这些元素,内容区域可以看成元素自身。

在 line box 模型中,inline boxes的高度直接受line-height控制,而真正的高度表现则是由每行众多的inline boxes(等于内部最高的inline box的高度),而这些line boxes的高度垂直堆叠形成了containing box的高度,也就是我们见到的<p>或者<div>标签的高度。

寻找“幽灵空白节点”

我们对图 1 中的代码稍作修改,如下:

图5

对于内联元素,vertical-align的默认值是baseline,也就是基线对齐,所以我们看到<div>标签中图片和span中的元素都是以字母x的下边缘对齐的

图6

字符 x 的自身是有高度的(由 font-size 属性决定)

图7

视图中sxx行框盒子的高度由line-height属性决定。

根据上面三次改动,我们可以发现,由于vertical-align的默认对齐属性和line-height导致图片需要和其他元素对齐时会导致出现图片下面的空隙。可是在图 1 中,<div>中只有<img>一个标签,却依然会出现底部的空隙,它是究竟在和谁对齐呢?所谓的“幽灵空白节点”究竟藏在了哪里?

莫着急,我们继续看 2 中的代码,并对其进行修改,如下图:

图8图9图10

对比上面三次代码改动,我们发现,当<span>中空白并且为inline box的时候,div中似乎依然有一个和类似 x 字符的空白节点,来撑起line box的高度。为了帮助我们理解,在这里我们可以把字符 x 看作为“幽灵空白节点”具像,它不占任何宽度,看不见也无法通过任何方式获取,但又似乎真实存在,它的大小会受font-size的影响。这就是我们一直寻找的“空白幽灵节点”。虽然隐藏的这么深,还是被我们一步一步给找了出来。

认识“幽灵空白节点”

在 HTML5 规范中有这样一句话:

"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'."
每个行框盒子都以一个具有元素的字体和行高属性的零宽度行内框开始。我们称这个假想的盒子为"支柱"。

HTML5 文档声明中,内联元素的所有解析和渲染表现就如同每个行框盒子的前面有一个“空白节点”一样。这个“空白节点”永远透明,不占据任何宽度,看不见也无法通过脚本获取,就好像幽灵一样,但又确确实实地存在,表现如同文本节点一样,因此,在张鑫旭大佬的《CSS 世界》一书中根据特点,将其称之为“幽灵空白节点”,也就是官方规范中的“strut”。

消除“幽灵空白节点”的影响

终于找到并认识了“幽灵空白节点”,那么对于这种无法获取却又真实影响我们样式的“节点”,我们应该如何消除它带来的影响?

  • 1、让vertical-align失效:vertical-align属性对于块级元素是无感的,因此我们只需要为元素设置dispalyblock即可;
  • 2、修改vertical-align属性值:修改其默认值baseline值为其他属性值,使其不再相对基线对齐;
  • 3、修改line-height的值:上面我们说过line-height的定义是两基线之间的距离,因此在上面例子中,图片下面的空隙,实际上是“幽灵空白节点”计算后的行高值和基线的距离,因此,只要line-height足够小,便可以消除图片下面的空隙。(注意这里是要在div上设置line-heght,然后让divinline boxes继承line-height属性)

结束语

CSS 世界看似简单,却隐藏了无数“奥秘”,在工作学习的过程中,去发现挖掘其中的奥秘,不仅可以让我们对其认知更加清晰,同时在我们编程的过程中可以更灵活的使用 CSS 去丰富我们的页面,让我们的页面更加血肉丰满,呈现更多的精彩。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK