视觉格式化模型

CSS 视觉格式化模型(Visual formatting model)

CSS 视觉格式化模型(Visual formatting model)是用来处理和在视觉媒体上显示文档时使用的计算规则,该模型是 CSS 的基础概念之一

视觉格式化模型会根据 CSS 盒子模型将文档中的元素转换为一个个盒子,每个盒子的布局由以下因素决定(学习视觉格式化模型,也就是要学习下面相关知识了):

  • 盒子的尺寸:精确指定、由约束条件指定或没有指定
  • 盒子的类型:行内盒子(inline)、行内级盒子(inline-level)、原子行内级盒子(atomic inline-level)、块盒子(block)
  • 定位方案(positioning scheme):普通流定位、浮动定位或绝对定位
  • 文档树中的其他元素:即当前盒子的子元素或兄弟元素
  • 视口尺寸与位置
  • 所包含的图片的尺寸
  • 其他的某些外部因素(例如:flex-box)

视觉格式化模型会根据盒子的包含块的边界来渲染盒子。通常,盒子会创建一个包含其后代元素的包含块,但是盒子并不由包含块所限制,当盒子的布局跑到包含块的外面时称为溢出(overflow)

视觉格式化模型定义了盒(Box)的生成,盒主要包括了块盒、行内盒、匿名盒(没有名字不能被选择器选中的盒)以及一些实验性的盒(未来可能添加到规范中)。盒的类型由display属性决定

ps:页面(文档树)可以想象成是由一个个 box 组合而成的,而"视觉格式化模型(Visual formatting model)" 是一套规则,将这些 box "布局"成访问者看到的样子。

盒子的生成

块级元素与块盒子

  • 当元素的CSS属性displayblocklist-itemtable时,它是块级元素 block-level;
  • 视觉上呈现为块,竖直排列;
  • 每个块级盒子都能参与块格式化上下文(block formatting context)的创建(overflow属性且值不是visible的块级盒子都是 BFC),而每个块级元素都能至少生成一个块级盒子,即主块级盒子(principal block-level box)。
  • 有一些元素,比如列表项会生成额外的盒子来放置项目符号,而那些会生成列表项的元素可能会生成更多的盒子。不过,多数元素只生成一个主块级盒子

行内级元素和行内盒子

  • 当元素的CSS属性display的计算值为inlineinline-blockinline-table时,称它为行内级元素;
  • 视觉上它将内容与其它行内级元素排列为多行;典型的如段落内容,有文本(可以有多种格式譬如着重),或图片,都是行内级元素;
  • 行内级元素生成行内级盒(inline-level boxes),参与行内格式化上下文(inline formatting context)的创建,同时参与生成行内格式化上下文的行内级盒称为行内盒(inline boxes)。所有display:inline的非替换元素生成的盒是行内盒;
  • 不参与生成行内格式化上下文的行内级盒称为原子行内级盒(atomic inline-level boxes)。这些盒由可替换行内元素,或 display 值为 inline-blockinline-table 的元素生成,不能拆分成多个盒;

匿名盒

匿名盒也有分匿名块盒与匿名行内盒,因为匿名盒没有名字,不能利用选择器来选择它们,所以它们的所有属性都为inherit或初始默认值;

块包含盒子可能只包含行内级盒子,也可能只包含块级盒子,但通常的文档都会同时包含两者,在这种情况下,就会在相邻的行内级盒子外创建匿名块盒子

如下面例子,会自动创建匿名块盒来包含相邻的行内级盒:

js 复制代码
<div>
    Some inline text //创建匿名块盒
    <p>followed by a paragraph</p>
    followed by more inline text. 创建匿名块盒
</div>

//可能变成这样
<div>
    <p>Some inline text</p> //创建匿名块盒
    <p>followed by a paragraph</p>
    <p>followed by more inline text.</p> 创建匿名块盒
</div>

另一种会创建匿名块盒子的情况是一个行内盒子中包含一或多个块盒子。此时,包含块盒子的盒子会拆分为两个行内盒子,分别位于块盒子的前面和后面。块盒子前面的所有行内盒子会被一个匿名块盒子包裹,块盒子后面的行内盒子也是一样。因此,块盒子将成为这两个匿名块盒子的兄弟盒子。如果有多个块盒子,而它们中间又没有行内元素,则会在这些盒子的前面和后面创建两个匿名块盒子

可以理解为,行盒中有一个快盒,整体效果就变成块盒了,只不过相邻的行盒会合并成一个匿名块盒

js 复制代码
<span>
    Some inline text //创建匿名块盒
    <p>followed by a paragraph</p>
    followed by more inline text. 创建匿名块盒
</span>

//可能变成这样
<p>Some inline text</p>
<p>followed by a paragraph</p>
<p>followed by more inline text</p>

块盒子、行盒子等概念

  • :block,一个抽象的概念,一个块在文档流上占据一个独立的区域,块与块之间在垂直方向上按照顺序依次堆叠。
  • 包含块:containing block,包含其他盒子的块称为包含块(后面有更详细介绍)。
  • 盒子:box,一个抽象的概念,由 CSS 引擎根据文档中的内容所创建,主要用于文档元素的定位、布局和格式化等用途。盒子与元素并不是一一对应的,有时多个元素会合并生成一个盒子,有时一个元素会生成多个盒子(如匿名盒子)。
  • 块级元素 :block-level element,元素的 displayblocklist-itemtable 时,该元素将成为块级元素。元素是否是块级元素仅是元素本身的属性,并不直接用于格式化上下文的创建或布局。
  • 块级盒子:block-level box,由块级元素生成。一个块级元素至少会生成一个块级盒子,但也有可能生成多个(例如列表项元素)。
  • 块盒子:block box,如果一个块级盒子同时也是一个块容器盒子(见下),则称其为块盒子。除具名块盒子之外,还有一类块盒子是匿名的,称为匿名块盒子(Anonymous block box),匿名盒子无法被 CSS 选择符选中。
  • 块容器盒子:block container box 或 block containing box,块容器盒子侧重于当前盒子作为"容器"的这一角色,它不参与当前块的布局和定位,它所描述的仅仅是当前盒子与其后代之间的关系。换句话说,块容器盒子主要用于确定其子元素的定位、布局等。

注意:盒子分为"块盒子"和"块级盒子"两种,但元素只有"块级元素",而没有"块元素"。下面的"行内级元素"也是一样。

  • 行内级元素 :inline-level element,displayinlineinline-blockinline-table 的元素称为行内级元素。与块级元素一样,元素是否是行内级元素仅是元素本身的属性,并不直接用于格式化上下文的创建或布局。
  • 行内级盒子:inline-level box,由行内级元素生成。行内级盒子包括行内盒子和原子行内级盒子两种,区别在于该盒子是否参与行内格式化上下文的创建。
  • 行内盒子:inline box,参与行内格式化上下文创建的行内级盒子称为行内盒子。与块盒子类似,行内盒子也分为具名行内盒子和匿名行内盒子(anonymous inline box)两种。
  • 原子行内级盒子:atomic inline-level box,不参与行内格式化上下文创建的行内级盒子。原子行内级盒子一开始叫做原子行内盒子(atomic inline box),后被修正。原子行内级盒子的内容不会拆分成多行显示。

由其他模型引入的盒子

除了行内格式化上下文和块格式化上下文之外,CSS 还定义了几种内容模型,这些模型同样可以应用于元素。这些模型一般用来描述布局,它们可能会定义一些额外的盒子类型:

  • 表格内容模型可能会创建一个表格包装器盒子和一个表格盒子,以及多个其他盒子如表格标题盒子等
  • 多列内容模型 可能会在容器盒子和内容之间创建多个列盒子
  • 实验性的网格内容模型或 flex-box 内容模型同样会创建一些其他种类的盒子

定位方案

在定位的时候,浏览器就会根据元素的盒类型和上下文对这些元素进行定位,可以说盒就是定位的基本单位。定位时,有三种定位方案,分别是常规流,浮动以及绝对定位。

  • 普通流:按照次序依次定位每个盒子
  • 浮动:将盒子从普通流中单独拎出来,将其放到外层盒子的某一边
  • 绝对定位:按照绝对位置来定位盒子,其位置根据盒子的包含元素所建立的绝对坐标系来计算,因此绝对定位元素有可能会覆盖其他元素

常规流(Normal flow)

  • 在常规流中,盒一个接着一个排列;
  • 块级格式化上下文 里面, 它们竖着排列;
  • 行内格式化上下文 里面, 它们横着排列;
  • positionstaticrelative,并且floatnone时会触发常规流;
  • 对于静态定位 (static positioning),position: static盒的位置是常规流布局里的位置
  • 对于相对定位 (relative positioning),position: relative,盒偏移位置由这些属性定义topbottomleftandright即使有偏移,仍然保留原有的位置,其它常规流不能占用这个位置。

浮动(Floats)

  • 盒称为浮动盒(floating boxes);
  • 它位于当前行的开头或末尾;
  • 导致常规流环绕在它的周边,除非设置 clear 属性;

绝对定位(Absolute positioning)

  • 绝对定位方案,盒从常规流中被移除,不影响常规流的布局;
  • 它的定位相对于它的包含块,相关CSS属性:topbottomleftright
  • 如果元素的属性positionabsolutefixed,它是绝对定位元素;
  • 对于position: absolute,元素定位将相对于最近的一个relativefixedabsolute的父元素,如果没有则相对于body

css基础框盒模型

每个盒子由四个部分(边界)组成,由内到外分别是:内容边界 Content edge内边距边界 Padding Edge边框边界 Border Edge外边框边界 Margin Edge

因此,控制内部布局的时候,一定要注意实际剩余的内容空间有多少

ps:实际设置子元素 100% 的时候,有 margin、padding 不正常的话,应该了解了吧,另外 padding 是会被 background 上色,但不要当成实际 content 哈😂

内容区域

内容区域(content area)由内容边界限制,容纳着元素的"真实"内容,例如文本、图像,或是一个视频播放器。它的尺寸为内容宽度(或称 content-box 宽度)和内容高度(或称 content-box 高度)。它通常含有一个背景颜色(默认颜色为透明)或背景图像。

如果 box-sizing 为 content-box(默认),则内容区域的大小可明确地通过 width、min-width、max-width、height、min-height 和 max-height 控制。

内边距区域

内边距区域(padding area)由内边距边界限制,扩展自内容区域,负责延伸内容区域的背景,填充元素中内容与边框的间距。它的尺寸是 padding-box 宽度 和 padding-box 高度。

内边距的粗细可以由 padding-top、padding-right、padding-bottom、padding-left,和简写属性 padding 控制。

边框区域

边框区域(border area)由边框边界限制,扩展自内边距区域,是容纳边框的区域。其尺寸为 border-box 宽度和 border-box 高度。

边框的粗细由 border-width 和简写的 border 属性控制。如果 box-sizing 属性被设为 border-box,那么边框区域的大小可明确地通过 width、min-width, max-width、height、min-height,和 max-height 属性控制。假如框盒上设有背景(background-color 或 background-image),背景将会一直延伸至边框的外沿(默认为在边框下层延伸,边框会盖在背景上)。此默认表现可通过 CSS 属性 background-clip 来改变。

外边距区域

外边距区域(margin area)由外边距边界限制,用空白区域扩展边框区域,以分开相邻的元素。它的尺寸为 margin-box 宽度和 margin-box 高度。

外边距区域的大小由 margin-top、margin-right、margin-bottom、margin-left,和简写属性 margin 控制。在发生外边距合并的情况下,由于盒之间共享外边距,外边距不容易弄清楚。

最后,请注意,除可替换元素外,对于行内元素来说,尽管内容周围存在内边距与边框,但其占用空间(每一行文字的高度)则由 line-height 属性决定,即使边框和内边距仍会显示在内容周围。

包含块

一个元素的尺寸和位置经常受其包含块(containing block)的影响

对于一些属性,例如 width, height, padding, margin,绝对定位元素的偏移值(比如 position 被设置为 absolute 或 fixed),当我们对其赋予百分比值时,这些值的计算值,就是通过元素的包含块计算得来。

大多数情况下,包含块就是这个父元素,但也不是总是这样,包含块的确定,通常有下面几种情况:

  • 常规元素和浮动元素父元素的内容盒

  • 绝对定位 absolute: 第一个定位祖先的填充盒(padding + content)

  • 固定定位 fixed:

    • 无变形祖先-往上找找不到使用了 transform 的父元素,其就是包含块就是视口
    • 有变形祖先-包含块有的祖先使用了 transform,就是变形祖先,包含块就是变形祖先

区块格式化上下文(BFC)

区块格式化上下文(Block Formatting Context,BFC)是 Web 页面的可视 CSS 渲染的一部分,是块级盒子的布局过程发生的区域,也是浮动元素与其他元素交互的区域,其用于决定了块盒子内部区域独立,保证了内部文档的正常排版,不受外部盒子等干扰

下列方式会创建块格式化上下文:

  • 文档的根元素 html。
  • 浮动元素(即 float 值不为 none 的元素)。
  • 绝对定位元素(position 值为 absolute 或 fixed 的元素)。
  • 行内块元素(display 值为 inline-block 的元素)。
  • 表格单元格(display 值为 table-cell,HTML 表格单元格默认值)。
  • 表格标题(display 值为 table-caption,HTML 表格标题默认值)。
  • 匿名表格单元格元素(display 值为 table(HTML 表格默认值)、table-row(表格行默认值)、table-row-group(表格体默认值)、table-header-group(表格头部默认值)、table-footer-group(表格尾部默认值)或 inline-table)。
  • overflow 值不为 visible 或 clip 的块级元素。
  • display 值为 flow-root 的元素。
  • contain 值为 layout、content 或 paint 的元素。
  • 弹性元素(display 值为 flex 或 inline-flex 元素的直接子元素),如果它们本身既不是弹性、网格也不是表格容器。
  • 网格元素(display 值为 grid 或 inline-grid 元素的直接子元素),如果它们本身既不是弹性、网格也不是表格容器。
  • 多列容器(column-count 或 column-width 值不为 auto,且含有 column-count: 1 的元素)。
  • column-span 值为 all 的元素始终会创建一个新的格式化上下文,即使该元素没有包裹在一个多列容器中(规范变更、Chrome bug)

ps: 里面最常见的设置基本上就是 overflow、flex 了,其他的了解即可

ps:使用浮动的话,需要注意在合适位置清除浮动

ps:独立块内部正常排版,仅仅代表内部能够正常排版,不受外部干扰,不代表着外部元素不能覆盖到我们的盒子上遮挡等,这个需要自己理解了😂

行内格式化上下文(IFC)

行内级元素生成行内级盒(inline-level boxes),参与行内格式化上下文(inline formatting context)的创建,同时参与生成行内格式化上下文的行内级盒称为行内盒(inline boxes),所有display:inline的非替换元素生成的盒是行内盒

行内格式化上下文(IFC),平时需要注意的也比较少,其是一个网页的渲染结果的一部分,各行内框(inline boxes)一个接一个地排列,其排列顺序根据书写模式(writing-mode)的设置来决定:

  • 对于水平书写模式 writing-mode: horizontal-tb ,各个框从左边开始水平地排列
  • 对于垂直书写模式 writing-mode: vertical-rl(vertical-lr),各个框从顶部开始向下垂直排列

带黑色边框的两个 <div> 元素组成了一个区块格式化上下文,其中的每一个单词都参与一个行内格式化上下文中,水平书写模式下的各个框水平地排列,垂直书写模式下的各个框垂直地排列

js 复制代码
.horizontal {
    writing-mode: horizontal-tb
}

<div class="horizontal">
  One Two Three
</div>

自动折断效果

行内格式化上下文中排版的行元素,即使是换一个标签(不管匿名不匿名,是inline行元素),也会正常换行折断显示,设置的 border 等也会被折断

例如:下面加入了 span 标签,但是 span 除了可以添加选择器属性之外,整体显示逻辑和旁边的一样,仍然是内部单次依次排列

js 复制代码
<div class="example">
  Before that night---<span>a memorable night, as it was to prove---hundreds of millions of people</span> had watched the rising smoke-wreaths of their fires without drawing any special inspiration from the fact."
</div>

padding-inline、margin-inline

行内元素一般不设置 padding-left,margin-left 之类的偏移,而是使用 padding-inline-start、padding-inline-end、margin-inline-start、margin-inline-end 表示更准确,毕竟其分为水平和垂直方向排雷,上下左右就不严谨了

js 复制代码
padding-inline-start: 20px;
padding-inline-end: 40px;
margin-inline-start: 20px;
margin-inline-end: 20px;

替换元素、非替换元素

替换元素是指浏览器会根据元素的标签和属性来决定其具体显示内容。这些元素的内容不受CSS视觉格式化模型的直接控制,且通常拥有固有尺寸(宽度、高度和宽高比)。常见的替换元素包括:

  • img:根据src属性的值加载并显示图片。
  • input:根据type属性的值显示不同类型的输入框(如文本框、单选按钮等)。
    • textarea、select、object、video、audio、canvas等。
  • 这些元素通常没有实际的内容,浏览器会根据元素的标签类型和属性来显示它们。例如,img标签通过src属性加载图片,而查看HTML代码时只能看到引用地址,看不到图片的实际内容‌

非替换元素是指其内容直接由浏览器显示给用户的元素。大多数HTML元素都是非替换元素

例如:div、p、span等。

最后

视觉格式化模型之前一般也会了解到 css 属性值计算,两个参考在一起理解更好了,如果再加上z方向上的层叠上下文相关理解,那就更好了,需要注意的是,虽然有重合,但这些基本上都是各自独立的概念,可以相互加深理解,不要当成一个概念😂

ps:有些中文翻译确实有些坑哈,需要注意,能和会对然都是can,但中文是两个意思,我总感觉不对😂

相关推荐
却尘2 小时前
Next.js 请求最佳实践 - vercel 2026一月发布指南
前端·react.js·next.js
ccnocare2 小时前
浅浅看一下设计模式
前端
Lee川2 小时前
🎬 从标签到屏幕:揭秘现代网页构建与适配之道
前端·面试
Ticnix2 小时前
ECharts初始化、销毁、resize 适配组件封装(含完整封装代码)
前端·echarts
纯爱掌门人2 小时前
终焉轮回里,藏着 AI 与人类的答案
前端·人工智能·aigc
twl2 小时前
OpenClaw 深度技术解析
前端
崔庆才丨静觅3 小时前
比官方便宜一半以上!Grok API 申请及使用
前端
星光不问赶路人3 小时前
vue3使用jsx语法详解
前端·vue.js
天蓝色的鱼鱼3 小时前
shadcn/ui,给你一个真正可控的UI组件库
前端
布列瑟农的星空3 小时前
前端都能看懂的Rust入门教程(三)——控制流语句
前端·后端·rust