(07)CSS 基本视觉格式化:① “块盒子”格式化 | CSS

2,312 阅读11分钟
原创:itsOli  @前端一万小时

本文首发于公众号前端一万小时

本文版权归作者所有,未经授权,请勿转载!

本文节选自“语雀”私有付费专栏前端一万小时 | 从零基础到轻松就业

❗️❗️❗️
以下链接为本文最新勘误篇章——《CSS 基本视觉格式化:① “块盒子”格式化》


1. 块级元素和行内元素分别有哪些? 空(void)元素有哪些?块级元素和行内元素有什么区别?
2. IE 盒模型和 W3C 盒模型有什么区别?
3. 在什么场景下会出现外边距合并?如何合并?如何不让相邻元素外边距合并?给个父子外边距合并的范例?

4. 关于 .item {width: 100%;},以下说法正确的是:
  ❌ .item 的宽度(包括左右 margin、左右边框、左右 paddingcontent)等于它父亲的宽度(包括左右
     margin、左右边框、左右 paddingcontent)。
  ❌ .item 的宽度(包括左右边框、左右 paddingcontent)等于它父亲的宽度(左右边框、左右 paddingcontent)。
  ❌ .item 的宽度(左右 paddingcontent)等于它父亲的宽度(左右 paddingcontent)。
  ✅ .item 的宽度(content)等于它父亲的宽度(content)。
  ❌ 如果设置了 * {box-sizing: border-box},.item 的宽度(包括左右边框、左右 paddingcontent)等于它父亲的宽度(左右边框、左右 paddingcontent)。

🙋上方面试题“参考答案详解”,请点击此处查看获取方式!



前言: 接下来的几篇系列文章我们讲一个东西——盒子(BOX)。

“盒模型”(Box Model)作为 CSS 看待元素的一种方式,CSS 将每个元素都看作由一个盒子表示。从某方面来说,对于初级、中级学习者的我们,大可将 CSS 的学习看作是对“盒子”的学习。
本篇我们将阐述最基本的理论知识,将“盒子”的方方面面一步步带到你的跟前。


1 什么是“盒子” 

“盒子 box”由 CSS 引擎根据文档中的内容所创建,主要用于文档元素的格式化、定位和布局等。

盒子与元素并不是一一对应的,有时多个元素会合并生成一个盒子,有时一个元素会生成多个盒子(如匿名盒子)。

一个完整的“盒子”中心有一个内容区(content area)。这个内容区周围有可选的 padding、边框和 margin。这些项之所以被认为是可选的,是因为它们的宽度可以设置为 0,实际上就是从“盒子”上去除这些项。

一个完整的“盒子”:


2 “视觉格式化模型”概述

CSS 视觉格式化模型(Visual formatting model)是用来处理和在视觉媒体上显示文档时使用的计算规则。

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

每个盒子的“布局”由以下因素决定(本篇文章和下一篇文章主要讲解第 ①、② 点,其属于“最基本的视觉格式化”,而对于剩下的要点,我们在接下来的系列文章中会挨个讨论):

① 盒子的尺寸:精确指定、由约束条件指定或没有指定;
② 盒子的类型:行内盒子(inline box)、行内级盒子(inline-level box)、原子行内级盒子(atomic inline-level box)、块盒子(block box);

③ 定位方案(positioning scheme):普通流定位、浮动定位或绝对定位;
④ 文档树中的其他元素:即当前盒子的子元素或兄弟元素;
⑤ 视口尺寸与位置;
⑥ 所包含的图片的尺寸;
⑦ 其他的某些外部因素。

每一个元素都是一个“盒子”,“盒子”可以嵌套“盒子”:

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

上图中,section 的包含块是 body,header、article、footer 的包含块是 section。

❗️区别:

  • “盒子模型”是处理盒子本身的内部属性——边距、边框等;
  • 而“视觉格式化模型”是用来处理这些盒子的摆放。

3 关于“盒子”我们一定需要知道的——基本视觉格式化

3.1 “盒子”的生成

盒子的生成是 CSS “视觉格式化模型”的一部分,用于从文档元素生成盒子。

盒子有不同的类型,盒子的类型取决于 CSS 的 display 属性——元素“角色”的改变。

3.1.1 “块级元素”和“块级盒子”

设置元素的 display 属性为 block、list-item 或 table 时,该元素将成为“块级元素”。

这些元素在正常流中时,会在其框之前和之后生成“换行”,所以处于正常流中的块级元素会“垂直”摆放

选择器 {
  display: block、list-item 或 table;
}

💡(“正常流”是指:西方语言文本从左向右、从上向下显示的方向,这也是我们熟悉的传统 HTML 文档的文本布局方向。注意,在非西方语言中,流方向可能不同。)

但,元素是否是“块级元素”仅是元素本身的属性,并不直接用于格式化上下文的创建或布局。

一个“块级元素”会被格式化成一个块(例如文章的一个段落),且默认按照垂直方向依次排列。

💡一个“块级元素”都会至少生成一个“块级盒子”,也有可能生成多个(例如列表项元素)。而“块级盒子”才会参与“块格式化上下文(block formatting context)”的创建。

3.1.2 “行内级元素”和“行内盒子”

当元素的 display 属性为 inline、inline-block 或 inline-table 时,该元素将成为“行内级元素”。

选择器 {
  display: inline、inline-block 或 inline-table;
}

这些元素不会在之前或之后生成“行分隔符”,所以处于正常流中的块级元素会 “水平” 摆放,它们是块级元素的后代。

显示时,它不会生成内容块,但是可以与其他行内级内容一起显示为多行。

💡同理,“行内级元素”会生成“行内级盒子”,该盒子同时会参与“行内格式化上下文(inline formatting context)”的创建。

🏆小总结: 相对更详细的“盒子”细分。

❗️注意: 一定要记得的是,display 之所以得名,是因为它影响的是元素如何“显示”,而不影响他本质上是何种元素,也就不能乱玩“嵌套关系”!

一个极端的反例就是:你不能让一个“链接”来包围一个“段落”。

<a href="http://…" style="display: block;">
<p style="display: inline;">这是一个错误的示例</p>
</a>

(对于生成不同类型“盒子”在实际项目中的运用,我们将在后续文章详细讨论。例如:怎样给“链接”加样式——生成导航栏、怎样给“表单”加样式等。)

3.2 不同类型“盒子”在格式化方式上也有所不同

本篇我们主要探讨 “块盒子”格式化,下篇文章讨论“行内盒子”格式化。

块盒子:

3.2.1 水平格式化

正常流中,“块盒子”的水平部分 = 其父元素的 width = 7 个属性之和——(margin-left ➕margin-right) ➕ (padding-left ➕padding-right)➕(border-left➕border-right)➕内容区自身 width。

在这 7 个属性中只有 3 个属性的值可以设置为 auto:width、margin-left、margin-right。其余的要不必须是确定的值,要不就是默认值 0。

🚀可详细分为以下 5 种组合:

  • 3 个都不是 auto:按 CSS 的术语来讲,这叫“格式化属性过分受限”,此时总会把 margin-right 强制为 auto 来适应父元素的宽度

  • 2 个不是 auto:是 auto 的将自动调整到适应父级元素的宽度;

  • margin-left、margin-right 是 auto:他们会自动设置为相等的长度,导致此元素在其父级元素中居中

  • 一个 margin 和 width 是 auto:auto 的 margin 会减为 0,width 自动填充其包含块;

  • 3 个都是 auto:两个外边距减为 0,width 会尽可能的宽(自动充满)。

❗️注意:由于水平 margin 不会合并,父元素的 padding、边框、margin 可能会对子元素带来“偏移”的影响。

3.2.2 垂直格式化

正常流中,“块盒子”的垂直部分 = 其父元素的 height = 7 个属性之和——(margin-top ➕margin-bottom) ➕ (padding-top ➕padding-bottom)➕(border-top ➕border-bottom)➕内容区自身 height。

同理,在这 7 个属性中只有 3 个的值可以设置为 auto:height、margin-top、margin-bottom。其余的要不必须是确定的值,要不就是默认值 0。

❗️不过,margin-top 和 margin-bottom 设置为 auto 也没有什么用,因为会被重置为 0。所以,想利用上下 margin 都是 auto 来垂直居中是不可能的。

垂直格式化的另一个重要方面是垂直相邻 margin 的合并。

这种合并行为只应用于 margin,如果元素有 padding 和边框,padding 和边框是不会合并的。当两个或更多垂直 margin 相遇时,他们将形成唯一一个 margin,这个 margin 的高度等于两个发生叠加的 margin 的高度中的较大者。

❗️注意:当一个元素包含在另一个元素中时,彼此相邻的 margin-bottom 和 magin-top 也会发生叠加,取较大者。

3.2.3 负 margin

🤔提问:水平或垂直方向各自 7 大属性相加要等于父元素的 width 或 height,那 margin 为负值会造成什么结果?

1. 水平方向
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>JS Bin</title>
</head>
<body>
<div>
  <p class="wide">How are you?</p>
  <p>Fine,thank you,and you?</p>
</div>
</body>
</html>

(1)类型1️⃣
div {
  width: 400px;
  border: 3px solid black;
}

p.wide {
  border: 1px dashed black;
  margin-left: 20px;
  width: auto;
  margin-right: -50px;
  background-color: yellow;
}

(2)类型2️⃣
div {
  width: 400px;
  border: 3px solid black;
}

p.wide {
  border: 1px dashed black;
  margin-left: 20px;
  width: 500px;
  margin-right: auto;
  background-color: yellow;
}

(3)类型3️⃣
div {
  width: 400px;
  border: 3px solid black;
}

p.wide {
  border: 1px dashed black;
  margin-left: -50px;
  width: auto;
  margin-right: 10px;
  background-color: yellow;
}

2. 垂直方向

(1)类型 ①:负 margin-top

🔗效果及源码链接

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>JS Bin</title>
</head>
<body>
<div>
  <p class="wide1">How are you?</p>
  <p class="wide2">Fine,thank you,and you?</p>
</div>
</body>
</html>
div {
  width: 400px;
  border: 3px solid black;
}

p.wide1 {
  border: 1px dashed black;
  margin-top: -20px; /* 修改这里的值来观察效果 */
  
  margin-right: 20px;
  margin-bottom: 30px;
  margin-left: 20px;
  width: auto;
  background-color: yellow;
}    
  
p.wide2 {
  border: 1px dashed black;
  margin-top: ;
  margin-right: 20px;
  margin-bottom: ;
  margin-left: 20px;
  width: auto;
  background-color: grey;
}

💡把段落 1 的 margin-top 设为“负”,它就被向上“拉”了 20 像素,且紧挨它的段落 2 也相应向上拉了 20 像素:

(2)类型 ②:负 margin-bottom

🔗效果及源码链接

div {
  width: 400px;
  border: 3px solid black;
}

p.wide1 {
  border: 1px dashed black;
  margin-top: px;
  margin-right: 20px;
  margin-bottom: -50px; /* 修改这里的值来观察效果 */
  
  margin-left: 20px;
  width: auto;
  background-color: yellow;
}   
    
p.wide2 {
  border: 1px dashed black;
  margin-top: ;
  margin-right: 20px;
  margin-bottom: ;
  margin-left: 20px;
  width: auto;
  background-color: grey;
}

💡把段落 1 的 margin-bottom 设为“负”,段落 2 会根据段落 1 底端的位置放置:

(3)类型 ③:正负 margin 合并

“块盒子”重叠的时候,如果垂直 margin 都为负值,浏览器会取两个 margin 绝对值的最大值;如果一个正 margin 与一个负 margin 合并,则会从正 margin 减去这个负 margin 的绝对值。

🔗效果及源码链接

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>JS Bin</title>
</head>
<body>
<div>
 <ul>
  <li>Fine</li>
  <li>thank you</li>
  <li>and you?</li>
 </ul>
<p>回答 How are you? 的方式!</p>
</div>
</body>
</html>
div {
  width: 400px;
  border: 3px solid black;
}

ul {
  border: 1px dashed black;
  margin-top: px;
  margin-right: 20px;
  margin-bottom: -15px; /* ① 修改这里的值来观察效果; */
  margin-left: 20px;
  width: auto;
  background-color: yellow;
}   
    
li {
  border: 1px dashed black;
  margin-top: ;
  margin-right: 20px;
  margin-bottom: 20px; /* ② 修改这里的值来观察效果。 */
  margin-left: 20px;
  width: auto;
  background-color: grey;
}

p {
  border: 1px dashed black;
  margin-top: -18px; /* ① 修改这里的值来观察效果。 */
  margin-right: 20px;
  margin-bottom: px; 
  margin-left: 20px;
  width: auto;
  background-color: yellow;
}

💡ul 和 p 重叠的 margin 都为“负”,则取绝对值较大者(-18px):

💡当上边的较大者(-18px)增加到 li 的最大正 margin 上(20px)时,就得到了 20px - 18px = 2px:



后记: 这篇我们学习了“块盒子”的格式化方式,下一篇我们接着这篇继续探讨“行内盒子”的格式化方式。在“行内盒子”格式化方式中,我们会谈到很多细小的基础知识,和本篇的学习方式一样,让我们尽可能的用代码、用图片来攻克它们。

祝好,qdywxs ♥ you!