04-基础篇:盒模型
juejin_logo copyCreated with Sketch.

前言

盒模型是CSS中最重要最核心的概念,整个概念基本上环绕在整个CSS知识体系中,所有样式与排版都围绕着它进行。理解盒模型才能更好地排版网页布局网页,后面遇到的排版与布局困难也能快速定位问题所在。

不要觉得简单而忽略它,往往越简单的东西蕴含的威力越大。换成人生道理就是说,不要小看一个人,TA虽然穿着短裤拖鞋,背后可能是拥有10栋出租楼的包租公或包租婆。

盒模型

盒模型又称框模型,指把文档节点看成一个盒子。它是一种网页设计思维模型。

HTML文档解析时,每个节点都会被描述为一个盒模型,然后一个盒子套进另一个盒子中,再根据各个节点相应CSS规则渲染为一个井井有条的网页。一个完整的网页就像惊喜爆炸礼盒,层层嵌套,直到最后一层出现惊喜为止。有女友的,赶紧买一个试试!

惊喜爆炸礼盒

组成

盒模型由以下属性组成,由外到内使用公式表示:box = margin + border + padding + content。除了content,其余属性都包括leftrighttopbottom等扩展属性。

  • margin:边距,外部透明区域,负责隔离相邻盒子
  • border:边框,内部着色区域,负责隔离边距与填充,包括widthstylecolor三个扩展属性
  • padding:填充,内部着色区域,负责扩展盒子内部尺寸
  • content:内容,以文本节点存在的占用位置

padding着色随background-color而变,可用background-clip隔离,在第10章会详细讲述。content不是属性,只是作为盒模型扩展理解使用。

理解

节点由外到内一层一层深入,通过上述公式组成一个完整的盒模型,所以在理解盒模型时记住这四个属性及其从外到内的顺序即可。换另一种方式理解,可把它看作快递包裹。两个快递包裹的间距就是margin,快递包裹的纸皮就是border,打开快递包裹,填充物料就是padding,把填充物料打开看到的物品,就是content。这样理解是不是特别简单?

类型

因为历史原因,盒模型分化出两种类型,分别是标准盒模型怪异盒模型,具体原因在第2章有提到。

CSS3中提供一个属性用于声明盒模型的类型,它就是box-sizing

  • content-box:标准盒模型(默认)
  • border-box:怪异盒模型

它不具备继承性,若全局统一盒模型,那只能使用*声明box-sizing了。建议使用reset.css中的声明方式。

标准盒模型

标准盒模型W3C标准,由margin + border + padding + content组成。与上述公式一样,节点的width/height只包括content,不包括padding/border

节点的尺寸计算公式如下。

  • 横向margin-[left/right] + border-[left/right]+ padding-[left/right] + width
  • 纵向margin-[top/bottom] + border-[top/bottom]+ padding-[top/bottom] + height

节点的宽高计算公式如下。

  • 横向width = width
  • 纵向height = height

怪异盒模型

怪异盒模型又称IE盒子模型,是IExplore标准,由margin + content组成。与上述公式不一,节点的width/height包括borderpaddingcontent

节点的尺寸计算公式如下。

  • 横向margin-[left/right] + width(包括border-[left/right]padding-[left/right])
  • 纵向margin-[top/bottom] + height(包括border-[top/bottom]padding-[top/bottom])

节点的宽高计算公式如下。

  • 横向width = border + padding + width
  • 纵向height = border + padding + height

IExplore中,若HTML文档缺失<!doctype html>声明则会触发怪异盒模型。

两者区别

通过代码演示可能会更清晰,width/height的范围也一目了然,其实两者区别在于width/height包不包括border/padding。把上述公式记清楚,两者区别就迎刃而解了。

.content-box {
	box-sizing: content-box;
	margin: 100px;
	padding: 50px;
	border: 10px solid #66f;
	width: 80px;
	height: 80px;
	background-color: #f66;
}
.border-box {
	box-sizing: border-box;
	margin: 100px;
	padding: 50px;
	border: 10px solid #66f;
	width: 80px;
	height: 80px;
	background-color: #f66;
}

盒模型类型

视觉格式化模型

上述盒模型都是平时了解的概念,若使用display对盒模型稍微加工则会进化为视觉格式化模型。

视觉格式化模型指在视觉媒体中处理与显示文档而使用的计算规则。它是一种CSS机制,由大量CSS规范组成,规定节点在网页中的排版。

块级元素

当节点的display声明为blocklist-itemtableflexgrid时,该节点被标记为块级元素。块级元素默认宽度为100%,在垂直方向上按顺序放置,同时参与块格式化上下文

每个块级元素至少生成一个块级盒或一个块容器盒,块级盒描述它与兄弟节点间的表现方式,块容器盒描述它与子节点间的表现方式。

一个块容器盒只包括其他块级盒或行内盒。可能一段代码中某个块容器盒同时包括块级盒与行内盒的情况,但实质上会产生一种新的匿名块盒解决该问题。

行内元素

当节点的display声明为inlineinline-blockinline-tableinline-flexinline-grid时,该节点被标记为行内元素。行内元素默认宽度为auto,在水平方向上按顺序放置,同时参与行内格式化上下文

当块级盒参与行内格式化上下文后会变成行内盒。另外还有一个称为匿名行内盒的概念,匿名行内盒与匿名块级盒的原理类似,都是浏览器自动生成的补充性盒。

以下简单使用一段代码理解匿名行内盒是如何产生的。

<p>我是<span>JowayYoung</span>,我的公众号是<span>IQ前端</span></p>

此时我是,我的公众号是就会生成一个匿名行内盒,然后与两个<span>一起处于<p>参与行内格式化上下文后的行内盒中并保持水平排列。

两者区别

上述概念可能有些绕口,若从两者区别中理解可能更易消化。

  • 互相转换
    • 块级元素转换行内元素:display:inline
    • 行内元素转换块级元素:display:block
  • 占位表现
    • 块级元素默认独占一行,默认宽度为父节点的100%,可声明边距、填充和宽高
    • 行内元素默认不独占一行(一行可多个),默认宽度随内容自动撑开,可声明水平边距或填充,不可声明垂直边距或宽高
  • 包括关系
    • 块级元素可包括块级元素与行内元素
    • 行内元素可包括行内元素,不能包括块级元素

格式化上下文

上述多次提到格式化上下文的字眼,那格式化上下文又是何方神圣?了解格式化上下文,就能更易理解上述内容了。

格式化上下文指决定渲染区域内节点的排版、关系和互相作用的渲染规则。简而言之,网页中有一个<ul>及其多个子节点<li>格式上下文决定这些<li>如何排版,<li><li>间处于何种关系,以及<li><li>间如何互相影响。

格式上下文由以下部分组成,其中块格式化上下文行内格式化上下文最重要。

上下文缩写版本声明
块格式化上下文BFC2块级盒子容器
行内格式化上下文IFC2行内盒子容器
弹性格式化上下文FFC3弹性盒子容器
格栅格式化上下文GFC3格栅盒子容器
读中文读起来有点拗口,干脆读英文缩写好了,有兴趣的同学自行查阅MDN了解其英文单词

为了防止有些同学对格式化上下文的概念越看越混乱,本章不会过多解说格式化上下文,但我会抽丝剥茧把格式化上下文的概念清晰化,更多的解读可自行查找相关资料深入学习。有时学习点到即止也未尝不是一件好事,过多解读反而达不到想要的效果。

块格式化上下文

BFC是网页中一个独立且隔离的渲染区域,容器中的子节点不会在布局中影响到外面的节点,反之亦然。

以下是我意译W3C标准与平时一些开发经验的总结所得,也结合一些自己对BFC的理解。

规则

  • 节点在垂直方向上按顺序排列
  • 节点的垂直方向距离由margin决定,相邻节点的margin会发生重叠,以最大margin为合并值
  • 节点的margin-left/right与父节点的左边/右边相接触,即使处于浮动也如此,除非自行形成BFC
  • BFC是一个隔离且不受外界影响的独立容器
  • BFC不会与同级浮动区域重叠
  • BFC在计算高度时其浮动子节点也参与计算

成因

  • 根节点:html
  • 非溢出可见节点:overflow:!visible
  • 浮动节点:float:left/right
  • 绝对定位节点:position:absolute/fixed
  • 被定义为块级的非块级节点:display:inline-block/table-cell/table-caption/flex/inline-flex/grid/inline-grid
  • 父节点与正常文档流的子节点(非浮动)自动形成BFC

场景

  • 清除浮动
  • 已知宽度水平居中
  • 防止浮动节点被覆盖
  • 防止垂直margin合并

margin塌陷在排版时稍微不注意就会出现,可用BFC的概念回答了。所谓的塌陷其实是两个BFC的相邻盒或父子盒互相作用时产生的效果,两个盒子会取相邻边最大margin作为相邻边的共用margin

在此再补充一些margin折叠的计算问题。

  • 两个盒子相邻边的margin都为正值,取最大值
  • 两个盒子相邻边的margin都为负值,取最小值,两者会互相重合
  • 两个盒子相邻边的margin一正一负,取两者相加值,若结果为负,两者会互相重合
行内格式化上下文

IFC的高度由容器中最大高度的子节点的实际高度确定,不受垂直方向的margin/padding的影响。另外,IFC中不能存在块元素,若加入块元素则会产生相应个数的匿名块并互相隔离,即产生相应个数的IFC,每个IFC对外表现为块级元素并垂直排列。

以下是我意译W3C标准与平时一些开发经验的总结所得,也结合一些自己对IFC的理解。

规则

  • 节点在水平方向上按顺序排列
  • 节点无法声明宽高,其margin/padding在水平方向有效在垂直方向无效
  • 节点在垂直方向上以不同形式对齐
  • 节点宽度由包括块与浮动决定,节点高度由行高决定

成因

  • 声明display:inline[-x]形成行内元素
  • 声明line-height
  • 声明vertical-align
  • 声明font-size
弹性格式化上下文

声明displayflexinline-flex时,节点会生成一个FFC的独立容器,主要用于响应式布局。

格栅格式化上下文

声明displaygridinline-grid时,节点会生成一个GFC的独立容器,主要用于响应式布局。

GFC有点像<table>,同为二维表格,但GFC有更丰富的属性控制行列、对齐以及更为精细的渲染语义与控制。不过因为兼容性不是特别好,所以也不会讲述基于GFC的格栅布局了。

文档流

文档流指节点在排版布局时默认使用从左往右从上往下的流式排列方式。窗体从上往下分成一行行且每行根据从左往右的顺序排列节点,其显著特性就是从左往右从上往下

类型

对于一个标准的文档流,可根据其特性对节点分类。

  • HTML级别
    • 容器级元素:<div><ul><li>
    • 文本级元素:<a><p><span>
  • CSS级别
    • 块级元素:<div><ul><li>
    • 行内元素:<a><p><span>
微观现象

即使是标准的文档流,也不排除存在一些小小的缺陷,以下是三种常见缺陷。

  • 空白折叠:换行编写行内元素,排版会出现5px空隙
  • 高矮不齐:行内元素统一以底边垂直对齐
  • 自动换行:排版若一行无法完成则换行接着排版

解决方案

空白折叠可能是最易出现的文档流微观现象。

<ul>
	<li></li>
	<li></li>
	<li></li>
</ul>
ul {
	text-align: center;
}
li {
	display: inline-block;
}

此时很多浏览器就出现5px空隙,解决方案也有很多种。

第一种,必须紧密连接节点。

<ul>
	<li></li><li></li><li></li>
</ul>

第二种,子节点声明margin-left:-5px

li {
	display: inline-block;
	margin-left: -5px;
}

第三种,父节点使用Flex布局

ul {
	display: flex;
	justify-content: center;
}
脱流文档流

脱流文档流指节点脱流正常文档流后,在正常文档流中的其他节点将忽略该节点并填补其原先空间。文档一旦脱流,计算其父节点高度时不会将其高度纳入,脱流节点不占据空间,因此声明浮动或定位后会对周围节点布局产生或多或少的影响。

文档流的脱流有两种方式。

  • 浮动布局float:left/right
  • 定位布局position:absolute/fixed

Float方式

节点声明float脱流时会让其跳出正常文档流,其他节点会忽略该节点并填补其原先空间。该节点的文本内容可不参与脱流效果,却会认同该节点所占据的空间并围绕它布局,这就是文字环绕效果的原理。

一句话概括:节点参与浮动布局后,自身脱流但其文本不脱流

Position方式

节点声明position脱流时(只有absolute/fixed)会让自身及其文本内容一起跳出正常文档流,其他节点会忽略该节点并填补其原先空间。absolute绝对定位是相对往上遍历第一个声明position:relative/absolute的祖先节点定位,若无此节点则相对<body>定位;fixed固定定位是相对浏览器窗口定位。

一句话概括:节点参与定位布局后,自身及其文本一起脱流

显隐影响

在正常文档流排版时,经常会声明display:nonevisibility:hidden控制节点的隐藏,display:none简称DNvisibility:hidden简称VH。上章提到DN/VH的区别,这次看看节点切入隐藏状态后,会存在何种差别。

  • 节点不可见但占据空间,显隐时可过渡:visibility:hidden
  • 节点不可见但占据空间,不可点击:visibility:hidden
  • 节点不可见不占据空间,可访问DOM:display:none
  • 节点不可见但占据空间,可点击:opacity:0
  • 节点不可见不占据空间,可点击:position:absolute; opacity:0
  • 节点不可见但占据空间,不可点击:position:relative; z-index:-1
  • 节点不可见不占据空间,不可点击:position:absolute; z-index:-1

层叠上下文

层叠上下文指盒模型在三维空间Z轴中的表现行为。每个盒模型存在于一个三维空间中,分别是平面画布的X轴Y轴与表示层叠的Z轴

在默认情况下,节点在网页中沿着X轴Y轴平铺,很难察觉它们在Z轴中的层叠关系。节点一旦发生堆叠,最终表现就是节点间互相覆盖。若一个节点包括层叠上下文,那它就拥有绝对的制高点,使用一个成语贴切表示就是鹤立鸡群,最终表现就是离屏幕观察者更近。

三维空间

声明position/z-index可让节点生成层叠上下文。很多同学可能一直认为z-index只是声明一个节点在Z轴的层叠顺序,值越高离屏幕观察者越近。其实这种认识不全面,还需注意以下情况。

  • z-index只在声明定位的节点中起效
  • 节点在Z轴的层叠顺序根据z-index、层叠上下文和层叠等级共同决定
层叠等级

层叠等级又称层叠级别,指节点在三维空间Z轴中的上下顺序。在同一层叠上下文中,它描述层叠上下文节点在Z轴中的上下顺序;在普通节点中,它描述普通节点在Z轴中的上下顺序。

普通节点的层叠等级优先由其所在的层叠上下文决定,层叠等级的比较只有在当前层叠上下文中才有意义,脱离当前层叠上下文的比较就变得无意义了。

成因

很多同学认为只有声明position/z-index才能让节点生成层叠上下文,其实不仅只有这两个属性,还有一些条件也能让节点生成层叠上下文。

  • <html>根结点
  • Flex布局中声明z-index不为auto的节点
  • Grid布局中声明z-index不为auto的节点
  • 声明position:relative/absolutez-index不为auto的节点
  • 声明position:fixed/sticky的节点
  • 声明mask/mask-image/mask-border不为none的节点
  • 声明filter不为none的节点
  • 声明mix-blend-mode不为normal的节点
  • 声明opacity不为1的节点
  • 声明clip-path不为none的节点
  • 声明will-change不为initial的节点
  • 声明perspective不为none的节点
  • 声明transform不为none的节点
  • 声明isolationisolate的节点
  • 声明-webkit-overflow-scrollingtouch的节点
层叠顺序

层叠顺序指节点发生层叠时根据指定顺序规则在Z轴中垂直显示。

脱流节点的层叠顺序

在同一层叠上下文中,节点根据z-index的大小从上到下层叠,若z-index一样则后面的节点层叠等级要大于前面的节点。脱流节点的层叠顺序看z-index的大小。

标准流节点的层叠顺序

标准流节点的层叠顺序稍微有点难记,我也未找到特别的记忆方式,只能死记硬背了。以下是层叠顺序从低到高的排列。

  • 层叠上下文的border/background
  • z-index<0的节点
  • 标准流内块级非定位的节点
  • 浮动非定位的节点
  • 标准流内行内非定位的节点
  • z-index:auto/0的节点
  • z-index>0的节点

标准流层叠顺序

留言
Ctrl + Enter
全部评论(66)
wzw的头像
删除
前端工程师
几个问题: 1. 块容器盒可不可以包括块容器盒
2. 块级元素和块级格式化上下文有什么区别啊
3.匿名盒子怎么可以看到呢
4.块容器盒同时包含块级盒与行内盒,有什么问题,为什么要用匿名盒解决
点赞
回复
wzw的头像
删除
前端工程师
什么是层叠上下文啊,不懂
点赞
回复
wzw的头像
删除
前端工程师
position:relative; z-index:-1,试了一下这个,可见啊
点赞
回复
鱼鱼爱吃胡萝卜的头像
删除
父节点与正常文档流的子节点(非浮动)自动形成BFC
这句话应该怎么理解啊?
点赞
回复
_听风语的头像
删除
大佬,我觉得box-sizing类型为content-box、border-box。举的这个例子不合适没事体现它们之间的区别
点赞
回复
盐焗乳鸽还要砂锅的头像
删除
大学生 @ 华中科技大学
记录一下,两个兄弟节点垂直margin合并的解决方案:给其中一个盒子添加一个父元素,并且设置为overflow:hidden;。我觉得可以理解为BFC对子节点起作用,如果直接给其中一个兄弟节点添加overflow是不会生效的
点赞
回复
张大尉的头像
删除
@坚果云
有一个图挂了,老大!
点赞
回复
jackliy的头像
删除
前端开发工程师
大佬,用overflow:hidden形成的2个bfc,垂直的margin好像不起作用,可以说下原因吗[呲牙]
点赞
回复
用户2933983946429的头像
删除
learn.shayhowe.com
开头讲的 横向/纵向/宽高/margin/padding这些,没有个图或者例子参考着看,对初学者不太友好,看起来像是总结。。。可以参考这篇文章一起看
点赞
回复
Samsara同学的头像
删除
菜🐔前端
请问下,p标签不是块级元素吗
「 CSS级别
块级元素:、、等
行内元素:、、等」
1
回复
亮亮ll的头像
删除
我为啥感觉写页面 每个块基本都用到了box-sizing:order-box 这样就固定整个外层的宽度了 所以一直有种 * {box-sizing:order-box} 应该会比较方便的想法 这个是为什么呢
点赞
1
删除
因为border-box这种更符合视觉效果 content-box称为标准盒模型并不严谨 理解为默认更合适.
点赞
回复
October_l的头像
删除
前端菜鸟 @ 滴滴出行
‘所谓的塌陷其实是两个BFC的相邻盒或父子盒相互作用时产生的效果’ 。这里出错了,塌陷问题可用BFC解决,而不是BFC的相邻盒子所产生的。这里应该改为 ’两个相邻盒或父子盒’。
3
回复
雪之樱的头像
删除
大三吖 @ 摸鱼摸鱼
我自己试了一下,padding应该是在水平垂直都会生效




Title

.span1 {
display: inline-block;
background: #abcdef;
}
.span2 {
padding: 10px;
background: #aa0000;
}
.span3 {
height: 100px;
background: #00aa00;
}
.span4 {
margin: 10px;
background: #0000aa;
}




2
3
4
展开
点赞
1
删除
(作者)
要看看是块元素还是行内元素
点赞
回复
ClariS的头像
删除
前端摸鱼
文中“absolute绝对定位是相对往上遍历第一个包含position:relative/absolute的祖先节点定位”的说法不太准确。绝对定位(position属性的值为absolute)应当是相对于最近的非 static 定位(static 为 position 属性的默认值)的祖先元素进行定位的。
1
回复
勤勉兔兔的头像
删除
BFC啊,块级格式上下文什么的,真的不需要死记硬背;多做几次静态页面、多写几个特效动画啊什么的自然就融会贯通了;就跟英语的语法一样。
点赞
1
删除
(作者)
是的,但是为了叙述还是的讲讲概念
点赞
回复
我不吃饼干的头像
删除
前端 @ 快手
我是真的觉得这章写的不怎么样,很多地方读了好几遍也不明白,“节点的margin-left/right与父节点的左边/右边相接触,即使处于浮动也如此,除非自行形成BFC”,“父节点与正常文档流的子节点(非浮动)自动形成BFC” 博主能不能用易懂的话表达一下,而不是用这么拗口的方式。应用也没有举例,“已知宽度水平居中;防止浮动节点被覆盖;防止垂直margin合并”完全不知道要如何去做。反而是盒模型margin、border、padding这种基础的一眼就能看懂的部分用了一大段蹩脚的比喻。虽然小册也不贵,但写的还不如掘金上免费的内容,让人有点失望。
7
4
删除
(作者)
原理的东西确实是很难用更易懂的方式去讲解,就像其他文章也一样,当然我也尽量用最简单的语言去表达,有些读者能看得懂,可能也有一些像你这样看不懂,或许是大家理解的不一样。另外你说小册的内容还不如免费内容,那你有把整本小册看完吗,为什么大多数读者说都能学到新知识和新技巧,那你也贴下你说的那些免费内容的链接出来,让大家一起仰望下,OK?
1
回复
删除
还没看完,近期抽空会读完,我的评论也仅针对这一章。
不懂的地方我说了,我确实是基础比较差,但我文章文章看的很仔细,看了三遍,确实看不懂,不知道怎么做。又找了其他相关的文章,才明白文章里一些地方是指什么,要怎么做,仅此而已。
原理的东西确实是很难用更易懂的方式去讲解,就像其他文章也一样,当然我也尽量用最简单的语言去表达,有些读者能看得懂,可能也有一些像你这样看不懂,或许是大家理解的不一样。另外你说小册的内容还不如免费内容,那你有把整本小册看完吗,为什么大多数读者说都能学到新知识和新技巧,那你也贴下你说的那些免费内容的链接出来,让大家一起仰望下,OK?
点赞
回复
查看更多回复
千竹的头像
删除
前端
前辈您好,块级格式化上下文——规则中 “每个节点的margin-left/right与父节点的左边/右边相接触,即使处于浮动也如此,除非自行形成BFC“ 这句话我还有一些疑问,希望能得到您的解答。
对于浮动,我理解的是,子元素设置 float(会形成一个 BFC),父元素要设置 overflow: hidden(子元素脱离常规流,父子之间不会自动形成BFC,需要在父元素上显示的设置才会形成一个 BFC。此时子元素的高度会参与父元素高度的计算),这时就符合 “每个节点的margin-left/right与父节点的左边/右边相接触”。
如果这样的话,那文章中提到的这句话后半句——除非自行形成BF,指的是什么情况呢?浮动不就会形成 BFC 嘛?那也是符合左边/右边相接触的,那还有哪种情况例外呢?
展开
1
回复
千竹的头像
删除
前端
问个问题:
“当节点的display声明为block、list-item、table、flex或grid时,该节点被标记为块级元素。块级元素默认宽度为100%,在垂直方向上按顺序放置,同时参与块格式化上下文”
这最后一句是不是还有“或弹性/格栅格式化上下文”呀?
1
1
删除
(作者)
这里只是拿块级元素和内联元素才将,实质上时会问包含的
点赞
回复
沉默抒怀者的头像
删除
前端er @ 腾讯
请问一下,浮动,定位等 脱离文档流之后,相当于是在z轴产生了一层新的层叠上下文,可以这样理解吗?
点赞
1
删除
(作者)
是的,本身也是这样
点赞
回复
demon3z的头像
删除
摸鱼仔
总体上还不错 定位描述上有部分不准确 贴上自己对于定位的部分总结
absolute 绝对定位 即相对于除了 static 定位的最近祖先盒子相对定位下的文档流进行定位,如果没有则是相对于视口移动,一般会覆盖在其他元素上,正常的文档流不保存元素的位置
fixed 固定定位 通过指定元素相对于视口的位置来指定元素位置,当元素祖先的 transform 属性不是 none 时,相对于视口改为该祖先,一般会覆盖在其他元素上,正常的文档流不保存元素的位置
1
回复

查看全部 66 条回复