image-20250823211541992

CSS 盒模型和布局

上一章我们介绍了CSS的基本语法和文本样式调整,相信各位小伙伴应该对CSS逐步上手了,这一章我们将继续深入学习CSS的盒子模型和布局,并带给大家有关UI设计方面的教学,让大家能够写出更加精美的页面。

盒子模型

盒子模型(Box Model)是网页布局中的一个核心概念,用于描述每个HTML元素在页面上的表现方式,它是我们后续学习网页布局的重要前置知识点。

什么是盒子

在HTML中,我们可以将任意一个元素视为一个矩形盒子(不仅仅是指之前介绍的div盒子)无论这个元素是行内元素还是块级元素:

html 复制代码
<span>十七张牌你能秒我</span>
image-20250823220331923

只要是一个元素,浏览器在渲染时都会将其当做一个矩形盒子渲染,而这样的一个盒子,一共包含4个部分,它由内容区域(Content)、内边距(Padding)、边框(Border)和外边距(Margin)组成,这种模型能够有助于我们理解和控制元素的布局和空间占用。

我们可以打开浏览器并按下键盘上的F12(或是鼠标右键点击"检查")在开发者工具中查看盒子的几个区域:

image-20250823222116815

默认情况下,由于我们并未设置任何内容,所以盒子的大小是由内容和标签属性来确定的。并且内边距、外边距、边框的宽度默认都是0,而这一部分,我们的主要学习目标就是如何去调整这几个部分来让元素的大小和样式随心变化。

内容区域

前面我们说到盒子的宽高是由内容来确定的,实际上用于控制盒子宽高的是widthheight属性,他们的默认值都是auto表示自动确定宽高,不过,针对于行内元素和块级元素来说,存在一些差异,在只考虑内容宽高的情况下:

  • **行内元素:**盒子的宽度由内部文本或其他行内元素的总宽度决定,盒子的高度由内部文本的字体大小和行高决定。
  • **块级元素:**盒子的宽度默认直接占满整行,盒子的高度由内部其他元素高度总和而决定。

在内部不存在任何元素的情况下,无论是行内元素还是块级元素高度都为0。行内元素的宽度也为0,块级元素的宽度依然是占满整行。

除了像这样通过内容来让盒子获得一个高度和宽度,我们也可以手动设置元素的高度和宽度:

html 复制代码
<div class="box">十七张牌你能秒我</div>
css 复制代码
.box {
  width: 200px;
  height: 100px;
}
image-20250824005453529

此时,这个盒子的宽就变成了200个像素点,高就变成了100个像素点,注意其内部元素依然是从上往下排列,文本内容会在顶端。但是注意,宽高设置仅针对于块级元素生效,行内元素是无法设置宽高的,我们可以将div换成span来观察:

html 复制代码
<span class="box">十七张牌你能秒我</span>

同时,我们在HTML阶段提到,块级元素会排斥其他元素,导致其他元素另起一行展示,这是块级元素的一大特性。同样的,虽然现在我们可以任意调整块级元素的大小,但是这并不影响它对于其他行内元素或块级元素的排斥性:

html 复制代码
<div class="box">大花洒大街上</div>
<span>圣诞节啊手动滑稽卡萨丁合计卡萨</span>
image-20250824012022933

除此之外,我们在HTML阶段还认识了一个img标签,它是一个行内块元素,所谓行内块,就是既具有行内元素的特性(比如不会占据一行宽度,而是穿插在文本中),也具有块级元素的特性(允许设置宽高)。因此,实际上对于图片这种行内块元素,widthheight也是可以生效的:

image-20250824010836526

可以看到,图片既不会排斥行内文本另起一行,又可以通过CSS设置宽高,这也是行内块元素的一个非常重要的性质(不过图片标签本身也自带了widthheight属性,在哪里设置都可以调整宽高,效果一样)

此外,我们也可以让盒子的宽度或高度自动适应内部元素的尺寸,比如内部文本只有200px,此时我们可以设置fit-content来自动适应内部宽度:

css 复制代码
.outer-box { width: fit-content; }

针对于盒子内部文本的自适应调整,除了fit-content之外,还有max-contentmin-content属性,由于不是很常用,这里不做详细介绍,具体使用效果请参阅MDN文档:https://developer.mozilla.org/zh-CN/docs/Web/CSS/Reference/Values/max-content

介绍完盒子的宽度和高度,我们接着来说一下嵌套的情况:

html 复制代码
<div class="outer-box">
  <div class="inner-box"></div>
</div>
css 复制代码
.outer-box {
  width: 400px;
  height: 100px;
  background-color: green;
}

.inner-box {
  background-color: yellow;
}

当两个盒子发生嵌套的时候,外部盒子的宽高并不会继承给内部盒子(不同于前面的文本相关属性,会自动向内继承)内部盒子的默认宽度遵循上面所说的规则。此时,我们可以手动设置内部盒子的宽高来指定内部盒子的大小:

image-20251130234907770

此外,除了设置一个固定值之外,我们也可以使用百分比的形式设置盒子的尺寸,比如,我们将其宽度设置为50%,高度设置为90%

image-20251130235155417

此时盒子将按照外部盒子的相对尺寸进行展示,比如外部盒子的宽度是200px,那么此时按照50%计算,就会变成100px的宽度进行展示,这在很多需要保持固定比例展示的情况下就显得非常灵活。当然,百分比的上限并非100,你可以设置一个超出100%的值,CSS的世界是非常自由且包容的,不要让固定思维限制了你的想象力:

image-20251130235426296

前面我们说到,由于块级元素的独特性质,即使我们不去手动设置它的宽度,也会默认占据一整行,相当于自带了width: 100%这个属性,不需要我们手动进行编写:

image-20251201000139013

在前面的学习中,我们知道,整个页面实际上是由body所包裹的,而body则是我们整个可见页面最外层的盒子:

image-20251130235704199

它是包含所有可见页面内容(文本、图片、链接等)的容器,而body的上一级就是页面的根元素html,默认情况下,htmldiv一样,高度初始为0且由内容撑开,但是宽度占据整个浏览器窗口。在默认情况下,<body> 默认也是一个块级元素,它的宽度会自动填满父元素<html>的宽度,高度同样是初始0然后内容撑开,并且浏览器的用户代理样式(后面介绍)通常会给它设置一个默认的margin(外边距,后面进行介绍),这也是为什么上面我们的盒子没有贴边展示的原因。

正是因为body默认占据整个页面的宽度,因此内部的所有块级元素根据性质,默认都会占据页面的一整行了。

**提问:**最外层的body作为整个页面的可视内容容器,是否也可以通过CSS修改它的大小呢?那html呢?

背景样式

前面我们介绍了盒子的大小如何进行控制,这一节我们接着来看盒子的背景。背景有两种形式:

  • **背景颜色:**使用某一种特定颜色作为盒子的背景。
  • **背景图片:**使用一张图片作为盒子的背景。

我们先来看最简单的一种背景颜色,可以使用background-color来设置颜色:

image-20250824025533064

和我们前面介绍的color属性一样,颜色可以使用十六进制表示或是使用rgbrgba函数来表示。如果我们未给background-color设置颜色时,它的默认值是transparent,代表透明无颜色。

有些时候,我们的页面中可能需要出现一些重点标注的词语或段落,此时使用背景颜色就可以达成非常不错的效果:

html 复制代码
<span>
  我是普通文本中的
  <span class="highlight">重点文本</span>
  需要标记出来
</span>
image-20250824030456812

我们接着来介绍一下背景图片,不同于纯色的背景颜色,背景图片可以让背景显示为一张图片的形式,我们可以将需要展示的图片放到项目目录下或是使用URL访问网络图片,就像我们使用img标签那样,我们需要使用background-image属性来:

css 复制代码
<div class="test-box"></div>
css 复制代码
.test-box {
  width: 200px;
  height: 200px;
  /* 使用url函数引用内部或外部图片地址 */
  background-image: url("https://img2.baidu.com/it/u=4082245214,2139971588&fm=253&fmt=auto&app=120&f=JPEG?w=889&h=500");
}

可以看到页面上确实出现了一个盒子,并且背景使用的是我们指定的图片:

image-20251130231430528

不过,这个图片只出现了一部分,这是因为默认情况下,背景图片显示的大小是按照图片实际像素大小展示的,这里的图片由于比我们设定的盒子宽高更大,因此一部分内容就超出了盒子导致无法展示。要解决这种问题也很简单,我们可以使用background-size属性来控制背景图片的尺寸:

css 复制代码
background-size: cover;  /* 尽可能缩放且保持比例 */

这里需要介绍一下background-size的几个预设值:

  • auto - 默认值,根据背景图片的固有尺寸来显示,如果图片没有固有尺寸,则根据容器尺寸来显示。
  • cover - 将背景图片放大或缩小,使其宽高比保持不变,并使其完全覆盖整个背景区域(优先选择窄的一边进行缩放并占满)
  • contain - 将背景图片放大或缩小,使其宽高比保持不变,并使其完全包含在背景区域内(优先选择宽的一边进行缩放,但可能出现重复展示)

使用cover后,背景图片确实进行了一定程度的缩小,可以看到更多内容,但是仍然有一部分内容被裁剪。而使用contain之后,背景图片的大小被强制缩小到盒子能够完整展示的大小,但是此时图片存在重复渲染的问题:

image-20251130232425909

要解决重复渲染的问题,我们可以使用background-repeat来防止背景图片进行重复渲染,默认情况下只要背景图片小于盒子尺寸,都会进行重复渲染,这里只需设置到no-repeat即可:

image-20251130232520417

此外,除了使用预设的几种background-size之外,我们也可以手动指定背景图片的宽高:

image-20251130233323482

除了设定固定值之外,我们也可以使用百分比来控制背景的宽度大小等,不过这里的百分比相对的是盒子的大小。

我们还可以通过background-position属性来控制背景图片在盒子中的位置:

image-20251130233813219

background-position 属性通常接受一个或两个值,第一个值定义水平位置 (X 轴),第二个值定义垂直位置 (Y 轴),比如:

水平关键字 (X) 垂直关键字 (Y) 描述
left top 将图片左上角对齐容器的左上角。
center center 将图片中心点对齐容器的中心点。
right bottom 将图片右下角对齐容器的右下角。

一些常用的组合示例:

  • 居中: background-position: center; (相当于 center center)
  • 左上角: background-position: left top;
  • 右下角: background-position: right bottom;
  • 垂直居中,水平靠左: background-position: left center; (或 center left,顺序通常是水平在前)

此外,我们也可以使用百分比值可以更灵活地定位,百分比是图片的中心相对于盒子的中心的偏移量:

  • background-position: 50% 50%;效果与background-position: center center是相同的。
  • background-position: 0% 0%;效果与left top是相同的。

当然,除了百分比也可以使用固定的尺寸,如px或是rem等,不过是以图片的右上角为原点开始进行计算,各位自行尝试就明白了,这里不详细介绍。

最后需要注意的是,background是以上所有属性的简写属性,和之前介绍的text-decoration一样,可以直接将上面这堆属性写到一起,就像这样:

css 复制代码
.test {
  background: red no-repeat url("https://img2.baidu.com/it/u=4082245214,2139971588&fm=253&fmt=auto&app=120&f=JPEG?w=889&h=500") 10px 20px / contain;
}
css 复制代码
.test {
  background-color: red;
	background-image: url("https://img2.baidu.com/it/u=4082245214,2139971588&fm=253&fmt=auto&app=120&f=JPEG?w=889&h=500");
	background-repeat: no-repeat;
	background-position: 10px 20px;
	background-size: contain;
}

顺序可以自由排列,但是注意background-positionbackground-size 必须用 / 分隔。

盒子的边框

前面我们介绍了盒子的大小,而边框也是盒子的一个重要属性,边框用来包围内容区域和内边距,形成一个可见的外框。要创建边框非常简单,需要两个必要的属性border-widthborder-style

image-20251201171358742

其中border-width用于控制边框的宽度,border-style用于控制边框的样式,这里详细介绍一下border-style属性:

  • none - 无样式,默认值
  • solid - 实线样式,一根普通的边框线,也是用的最多的一种
  • dashed - 虚线形式
  • dotted - 点线形式,一堆小点点围绕盒子
  • double - 双实线形式,两条实线围绕盒子(边框宽度至少需要2-3px才能正确显示)
  • ridge/groove - 相框形式,比较古老的样式,不符合现代审美

默认情况下,浏览器会为边框设置黑色作为默认颜色,我们也可以手动控制边框的颜色,可以使用border-color设置边框颜色:

image-20251201182703049

通过为盒子添加边框,可以让盒子的边界感更加明确,视觉上更加凸显。和上面一样,border属性是以上三者的简写属性,我们可以直接写成这样:

css 复制代码
.test { border: red 1px solid; }
css 复制代码
.test { 
	border-width: 1px;  /* 原来的写法 */
	border-color: red;
	border-style: solid;
}

当然,为了更加灵活地设置盒子边框,我们也可以针对盒子的四边单独进行边框样式设置,比如现在我们只需要设置盒子的上边框,我们可以使用border-top属性:

image-20251201185704939
css 复制代码
.test {
	border-top: red 1px solid;
  /* 或是拆分成下面的写法 */
  border-top-color: red;
  border-top-width: 1px;
  border-top-style: solid;
}

除了border-top之外,我们还可以使用border-bottomborder-leftborder-right分别控制下左右边距的样式。

需要注意的是,如果我们在一个地方同时使用了边框总设置和单边设置,此时会按照声明的顺序进行覆盖,比如:

css 复制代码
.test-box {
  border: green 2px solid;
  border-top: red 2px solid;  /* 后者会覆盖前者的设置 */
}

此时就会出现,三边为绿色边框顶部为红色边框的情况。当然,除了边框样式支持这样的写法之外,后续遇到的很多属性都是支持这样进行总体+特例进行设置的。

这里需要特别注意的是,默认情况下(非IE浏览器)盒子的边框会使得盒子的大小进一步增加:

image-20251201222808401
css 复制代码
.test-box {
  width: 300px;
  height: 100px;
  background-color: cornflowerblue;
  border: red 2px solid;   /* 由于四边都增加了一个2px的边框,导致盒子最终变成 304x104 的大小了 */
}

除了为盒子设置边框外,我们还可以对盒子进行进一步修饰,比如现在各大操作系统争先使用的圆角设计。我们可以使用border-radius属性来将盒子的四个角磨圆,看起来不那么尖锐甚至视觉上会很舒服:

image-20251201191953323

border-radius的尺寸决定了圆角的半径大小,注意,如果圆角半径设置过大,就会使得盒子的四角变成完全的圆形:

image-20251201220837917

此时的设定为border-radius: 100px;这代表盒子的圆角半径设定为100px的宽度,这已经远超四角能展示的最大半径了(这里盒子的高度是100px,由于上下都要做圆角处理,因此四角的圆角半径最大只能达到50px)因此就会按照盒子能够达到的最大半径进行展示。

和边框一样,我们可以单独控制某一个角的圆角效果:

image-20251201221311445

比如我们需要控制左上角的圆角半径,就可以使用border-top-left-radius属性来单独控制左上角的圆角半径效果,同样的,我们也可以使用border-top-right-radiusborder-bottom-left-radiusborder-bottom-right-radius等属性来控制,或是直接使用下面的写法:

css 复制代码
.test {
  border-radius: 100px 10px; /* 2 个值:分别作用于(左上+右下)、(右上+左下) */
  border-radius: 100px 20px 10px; /* 3 个值:分别作用于(左上)、(右上+左下)、(右下) */
  border-radius: 50px 20px 10px 30px; /* 4 个值:分别作用于四个角(顺时针) */
}

除了设置一个固定大小之外,我们也可以通过百分比的形式进行设置,百分比的形式会同时考虑盒子角的两条边,分别计算两边的圆弧半径,形成椭圆的效果:

image-20251201222409266

这里就分别按照长和宽的20%进行圆角处理,反正我个人觉得这种方式调出来的圆角是很难看的。当然如果希望固定值也能实现分别调整两边的圆角半径,也可以使用/分割:

css 复制代码
.test { border-radius: 10px 20px 30px 40px / 5px 15px 25px 35px; }

内边距

我们接着来介绍盒子模型中最重要的边距属性,分为内边距和外边距两个属性。

我们先来研究一下内边距,内边距指的是 内容(content)与边框(border)之间的空白区域,可以使用padding属性进行设置:

css 复制代码
.test-box {
  width: 200px;
  height: 50px;
  background-color: cornflowerblue;
  padding: 20px;  /* 控制盒子的内边距 */
  border: red 2px solid;
}
image-20251201233317443

可以看到,盒子的实际区域和边框之间出现了一个20px宽度的空白区域,和边框一样,默认情况下它会使得盒子变大。

我们可以尝试在盒子内添加一些文本之类的便于观察:

html 复制代码
<div class="test-box">
  <span>你干嘛,能不能不要再黑我们家鸽鸽了</span>
</div>
image-20251201233457000

由于设置了padding属性,盒子具有了内边距,因此实际的内容显示区域会距离盒子的边缘会有些距离(注意内边距也算作盒子大小的一部分,我们为盒子设置的背景会包含在内边距的尺寸范围内)即使我们去掉边框也是一样的效果:

image-20251201233641347

这对于一些卡片类型的界面元素来说,就非常合适了,我们会在接下来的UI设计课程中为大家介绍如何编写一个漂亮的卡片。和我们之前介绍的border属性一样,我们也可以单独控制四个方向的边距:

css 复制代码
.test {
  padding-top: 10px;
  padding-right: 20px;
  padding-bottom: 10px;
  padding-left: 20px;
}

或是使用简写写法独立设置:

css 复制代码
.test {
  padding: 10px;             /* 四边都是10px */
  padding: 10px 20px;        /* 上下10px,左右20px */
  padding: 10px 20px 30px;   /* 上10px,左右20px,下30px */
  padding: 10px 20px 30px 40px; /* 上10px,右20px,下30px,左40px */
}

有些时候,我们在计算盒子宽度的时候,往往还需要考虑边框和内边距的宽度,这就给我们造成了很大的麻烦,比如我们希望创建一个大小为100x300的盒子(包含内边距和边框在内)我们就得先去计算边框和内边距的宽度,再进行减法运算。那有没有能够节省我们计算时间同时又能满足我们要求的方案呢?

这里就需要提到一个比较特殊的属性box-sizing了,它是 CSS 中用来控制盒子模型宽高计算方式的属性,它决定了元素的 width、height 是如何计算的。默认情况下,盒子模型采用的是content-box模式,width / height 只包含内容区(content),不包含 padding 和 border,所以当我们添加边距和边框时,盒子的尺寸会变大:

元素实际宽度/高度 = content + padding + border

如果希望边距和边框算在盒子总宽度之内,我们可以使用border-box作为值:

css 复制代码
.test-box {
  width: 300px;
  height: 100px;
  background-color: cornflowerblue;
  padding: 20px;
  border: 3px solid red;
  box-sizing: border-box;   /* 此时盒子的高度就是height设定的高度 */
}

而盒子内容的实际大小,将会按照如下公式来计算:

content = width/height - padding - border

比如这里的盒子大小就是:

image-20251202000257794

这样,我们就可以只关心盒子最终大小,而无需再花时间去计算边框和内边距的尺寸了。

除了使用固定大小作为padding的值之外,我们也可以使用百分比的形式,但是注意,padding 的百分比始终相对于父元素的宽度来计算,不管是水平还是垂直方向。

image-20251202170218442

最后需要注意的是,对于行内元素来说,内边距的使用受到一些限制,我们在前面说到,行内元素高度是由文本和行高决定的,即使我们手动对齐进行高度设置也是无效的,不同于块级元素和行内块元素,我们即使可以通过内边距的形式去撑大盒子,也无法改变行内元素最终展示的高度:

html 复制代码
<span style="padding: 20px">我是一段文本内容</span>
image-20251202155511221

可以看到,行内元素虽然也可以通过设置padding属性来实现内边距效果,但是仅限于padding-leftpadding-right会正常生效并撑开内容区域,而上下的内边距会出现一些问题,padding-toppadding-bottom虽然会生效,但不影响行高设定,不会像块级元素或是行内块元素那样把周围元素推开:

html 复制代码
<span style="padding: 20px">我是一段文本内容</span>
<span>我是另一段文本内容,很长很长很长很长很长很长很长很长很长很长很长很长很长很长</span>
image-20251202155748750

因此,针对于行内元素来说,盒模型不参与垂直方向上的布局计算。不过有意思的是,由于我们通过内边距的形式强行撑大了盒子,行内元素撑开的盒子大小依然是有效的,也就是说背景还是会按照被撑开的大小展示:

html 复制代码
<span style="padding: 20px;background-color:red;">我是一段文本内容</span>
image-20251202160215704

那么有没有办法让行内元素也可以像块级元素那样正确展示padding的效果呢?后面我们会为大家介绍如何在行内和块级元素之间进行转换。

外边距

介绍完内边距之后,我们接着来介绍一下外边距margin,它也是 CSS 盒模型中的一部分,用来控制元素外部与其他元素之间的距离。和padding不同,它不会影响元素本身的大小,而是影响它与周围元素的间隔。我们可以使用margin属性来设置盒子的外边距:

html 复制代码
<body>
  <div class="test-box">我是一号盒子</div>
  <div class="test-box" style="margin: 10px">我是二号盒子</div>
</body>
image-20251202154625347

可以看到,在设置外边距之后,盒子以外的部分和其他盒子之间或是父元素的边框之间存在一定的间距,这就是外边距。

和内边距一样,我们可以自由设置四个方向的外边距:

css 复制代码
.test {
  margin: 20px; /* 这意味着四个方向(上、右、下、左)的外边距都是 20 像素 */
	margin: 20px 10px; /* 上下外边距为 20 像素,左右外边距为 10 像素 */
  margin: 20px 10px 15px; /* 上外边距为 20 像素,左右外边距为 10 像素,下外边距为 15 像素 */
  margin: 20px 10px 15px 5px; /* 上外边距为 20 像素,右外边距为 10 像素,下外边距为 15 像素,左外边距为 5 像素 */
}

或是单独设置每一个:

css 复制代码
.test {
  margin-top: 20px;
  margin-bottom: 20px;
  margin-left: 20px;
  margin-right: 20px;
}

同样的,行内元素也可以设置外边距来与其他元素保持距离:

html 复制代码
<span style="margin-right: 20px">我是一段文本内容</span>
<span>我是另一段文本内容,很长很长很长很长很长很长很长很长很长很长很长很长很长很长</span>
image-20251202165338290

但是注意,和前面的内边距一样,即使我们为行内元素添加上下外边距,也是没有任何效果的:

image-20251202165655448

同样是只有左右会生效,因为行内元素高度始终是由文本和行高决定,虽然盒子模型上显示确实存在上下的外边距设定,但是最终并不会生效。而块级元素和行内块元素则不受到影响。

除了设定固定尺寸外,我们也可以使用百分比或是一些预设值,使用百分比和padding效果一样,都是按照父元素的宽度进行计算。不过,这里有一个比较特殊的值,我们可以将margin设置为auto来实现居中效果:

image-20251203163358971

margin设置为auto时,浏览器会自动计算盒子两边距离父盒子边框的宽度,从而实现横向居中的效果。注意,auto只会影响横向效果,这对纵向是没有作用的。

最后还有一个比较大的坑点需要注意,首先是盒子之间的间距可能会存在问题,我们看下面这个例子:

html 复制代码
<div class="test-box"></div>
<div class="test-box"></div>
css 复制代码
.test-box {
  width: 200px;
  height: 50px;
  margin: 20px;   /* 此时两个盒子都具有外边距 */
  background-color: cornflowerblue;
}
image-20251203171508542

我们发现,虽然两个盒子都设置了外边距,但是在最终展示出来的效果上,却只存在一个外边距(上面盒子底部的外间距和下面盒子顶部的外间距重合了)难道不应该是两个外边距加一起吗,应该是40px才对啊,为什么这里会重合呢?

这种情况我们一般称为**"margin折叠",它往往出现在相邻块级元素**的垂直方向上,就和上面图中一样(这种情况针对行内块和行内元素无效,仅块级元素有效)

此外,除了相邻盒子存在折叠现象外,在父子元素之间也会出现这种情况:

html 复制代码
<div class="outer-box">
  <div class="inner-box"></div>
</div>
css 复制代码
.outer-box {
  width: 200px;
  height: 50px;
  margin-top: 10px;
  background-color: cornflowerblue;
}

.inner-box {
  width: 100px;
  height: 30px;
  background-color: red;
  margin-top: 20px;
}
image-20251204155130283

当我们在内层盒子中使用margin-top属性时,它的会直接连带父盒子一起产生边距效果(仅限margin-top会出现这种情况)这种特性有些时候非常恶心,导致我们设置边距出现问题。不过,针对这种情况来说,只要父盒子满足以下要求,就可以阻止折叠行为:

  1. 父元素有 padding-top(哪怕是 1px)
  2. 父元素有 border-top(哪怕是透明边框)
  3. 父元素形成新的块格式化上下文(BFC,后续会介绍,比如设置overflow属性)

此外,即使是在同一个盒子下,自己本身的上下外边距也会出现折叠的情况:

html 复制代码
  <div class="outer-box"></div>
  <div>我是下面的盒子</div>
css 复制代码
.outer-box {
    width: 200px;
    margin: 20px 0;
}
image-20251211174135261

最后,这里总结一下发生 margin 折叠的三种情况

  1. 相邻的兄弟元素: 相邻的块级兄弟元素,上面元素的 margin-bottom 和下面元素的 margin-top 会发生折叠。
  2. 父元素和第一个子元素: 如果父元素没有上边框、上内边距,并且没有内容将它和它的第一个子元素分开,那么父元素的 margin-top 和第一个子元素的 margin-top 会发生折叠。
  3. 空的块级元素: 如果一个块级元素没有内容、paddingborder,并且 heightauto,那么它自己的 margin-topmargin-bottom 会发生折叠。

用户代理样式

还记得我们在一开始的时候介绍到,引入CSS的方式主要有三种,分别是:

  1. 内联样式/行内样式 (Inline CSS)直接在HTML元素的style属性中编写CSS代码。
  2. 内部样式 (Internal CSS)在HTML文档的<head>标签内使用<style>标签定义CSS规则。
  3. 外部样式 (External CSS)将CSS代码写在独立的.css文件中,然后通过<link>标签引入到HTML页面中。

此外,在计算机发展的早期,浏览器本身为了能让一些缺少自定义CSS样式的网站有一个大致的排版,通常会有一些自己默认的样式设置,比如p标签就存在浏览器预设的一些默认边距设置:

image-20251204175738933

包括我们前面介绍的body标签,在Chrome下同样具有默认外边距设置,我们可以选中body标签,然后查看外边距:

image-20251205123859667

可以看到,body的默认样式实际上是来源于用户代理样式表的。

虽然用户代理样式表很多情况下能够帮助一些没有CSS样式的网站更好看地显示内容,但是在有些时候,不同浏览器的用户代理样式不一样,可能会导致网页的某些元素以不一样的尺寸或边距进行展示,从而出现不同用户看到的页面样式不同的情况。因此,为了避免这个问题,我们可以引入一些用于消除不同浏览器用户代理样式差异的CSS文件,从而实现统一。

这里我们使用前面提到的link标签进行引入:

html 复制代码
<link rel="stylesheet" href="css/normalize.css">

可以看到normalize.css将一些常见的元素样式进行了统一,从而消除不同浏览器之间的差别:

css 复制代码
html {
  line-height: 1.15; /* 统一行高设置 */
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;  /* 统一消除了body的外边距 */
}

...

不过,我们每次引入CSS都要像这样先去下载对应的文件再进行导入实在是太累了,有些时候我们也可以尝试使用一下不同厂商CDN为我们提供的远程地址。

**CDN(Content Delivery Network)**是一组分布在全球各地的加速节点,用来缓存和分发网站的静态资源,让用户能从最近的节点加载内容,从而大幅提升访问速度。

一些大厂会将常用的CSS、JS、字体等资源存放到CDN加速节点上,我们可以直接使用CDN上的资源,这样不仅可以减少自己服务器数据传输消耗的带宽,也能利用这些加速资源让用户更快加载网站。

其中比较常见的CDN提供站点有:Cloudflare、百度、字节、阿里云、Akamai等。这里我们以Cloudflare的CDN加速站点为例,引入normalize.css文件,首先前往官网:https://www.jsdelivr.com,接着搜索normalize.css

image-20251205125225419

我们直接选择第一个,然后找到引入CSS的HTML标签:

html 复制代码
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/normalize.css@8.0.1/normalize.min.css">

接着就可以将其加入到我们的页面上了,效果和本地引入是一样的。

滚动区域

在网页开发中,内容超出容器范围是非常常见的情况,而当内容超出容器但我们又希望保持布局整洁、不让内容溢出到外面时,就需要使用 **滚动区域(scroll area)**滚动区域本质上是利用 CSS 的 overflow 属性控制内容的可视范围。

默认情况下,如果内容超出了盒子的大小,会直接显示到盒子外部:

html 复制代码
<div class="box">
  我是盒子里面的字,很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长
</div>
css 复制代码
.box {
  width: 200px;
  height: 100px;
  background-color: #f0f0f0;
  white-space: nowrap;
}

可以看到文字已经跑到盒子外面去了:

image-20251209155812692

默认情况下盒子的overflow属性为visible,表示当内容溢出盒子后,依然会展示出来。我们可以将其修改为auto来实现内容溢出时自动添加滚动条:

css 复制代码
.box {
  width: 200px;
  height: 100px;
  background-color: #f0f0f0;
  white-space: nowrap;
  overflow: auto;  /* 未溢出时不展示滚动条,溢出时自动展示 */
}
image-20251209160202372

可以看到,现在盒子里面的文本超出部分被自动隐藏,需要通过盒子底部出现的滚动条滚动查看。此外,我们也可以将auto改为scroll来让滚动条常驻。

如果我们希望直接隐藏掉内容超出的部分,也可以使用以下两种值:

  • hidden - 直接隐藏超出内容(但是可以被JS或浏览器强制滚动)
  • clip - 裁切掉超出内容(同上,但是不能强制滚动)

虽然这两种都可以实现超出部分的隐藏效果,但是前者依然会创建滚动容器(你可以理解为依然可以滚动,只是滚动条被隐藏了)而后者是实打实的按照盒子宽度对内容进行了裁切,超出盒子部分相当于不存在了(注意clip选项比较新,老旧浏览器可能不支持)所以在大部分需要裁切内部元素且不保留滚动效果的情况下,选择clip性能会更好。

想要查看哪些CSS特性受支持可以访问 https://caniuse.com 一键查询

除了统一设置滚动外,我们也可以针对于垂直和水平方向进行分别控制滚动条:

css 复制代码
.box {
  width: 200px;
  height: 100px;
  overflow-y: auto;   /* 只允许垂直滚动 */
  overflow-x: hidden; /* 禁止水平滚动 */
}

最后我们再来研究一下bodyhtml的滚动,和我们自己创建的盒子不同的是,当元素超出整个页面的大小时,浏览器窗口会自动创建一个滚动条,这里我们把盒子高度弄大一点:

css 复制代码
.box {
  width: 200px;
  height: 5000px;
  background-color: #f0f0f0;
}

由于bodyhtml的高度会被内容撑开,此时页面无法容纳全部信息。在默认情况下,当页面内容超出视口高度时,会自动显示滚动条,我们不需要对任何标签设置overflow属性,为了方便开发者设置,这个滚动条默认是附加在html上的,实际的滚动容器是html

因此,你可以为htmlbody也可以但部分浏览器不支持)设置hidden属性来实现隐藏超出视口大小部分:

css 复制代码
html {
  overflow: hidden;
}

此时浏览器窗口的滚动条就消失了,无法滚动页面。

UI设计系列(课程一)

欢迎来到我们的UI设计系列课程,在本系列中,我们将会针对一些现代化网站常见的UI样式进行讲解剖析,让大家有能力设计出一个更漂亮,更有个性,更符合现代化设计的网站。现在很多开发者的通病,就是缺乏一个良好的审美,和页面UI设计思路,导致最终设计出来的网站页面样式粗糙,用户操作复杂,层次不明确,文本看不清等问题。

UI设计就像是商品的包装,一个优秀的UI设计往往更能吸引用户的眼光,我们就从用现在的最多的卡片元素开始介绍,这里有两个卡片:

image-20251205131219465

第一眼看来,各位觉得是1号卡片好看,还是2号卡片好看呢?相信绝大部分用户可能会更加青睐2号卡片,那么2号卡片为什么相比1号卡片,看起来更舒服呢?我们这节课就从几个角度来分析一下原因。

  1. 卡片的背景和边框:卡片的背景是最为重要的一个因素,既然要让用户知道我们这里展示的是一个卡片,那么卡片肯定需要有一个自己的"边界",而卡片的背景就是一个很好的边界划分,因为外部是白色背景,此时我们只需要将卡片设置为一个区别于白色的颜色即可,这样卡片的形态就出来了。但是注意,卡片的颜色需要有一定的对比度,否则看起来就很容易和内容或是外部区域的颜色打架:

    image-20251205142424512

    上面的例子中,一号卡片由于颜色与页面背景相似,看起来就像是在一起的一样,三号卡片颜色虽然和外部形成鲜明对比,但是由于颜色过深,导致卡片内部字体可读性变差。因此,卡片的背景颜色需要保证在有对比度的情况下同时还要兼顾内容的可读性。

    当然,有些场景下可能浅色背景确实比较好看,背景越浅,内容就能够越凸显,可读性就更好,我们也可以保留浅色背景设计,为卡片设置一个边框来增加边界感:

    image-20251205160304883

    现在看来,是不是一号卡片就不像之前那样没边界感了,同时卡片的背景也能够更加凸显内容,这就是边框的作用。除了边框外,我们也可以使用盒子的阴影来增加边界感,有关阴影的设置我们会在后续的章节中进行介绍。

  2. 卡片的边距:除了背景颜色,边距也是影响卡片美观的一个非常重要因素,当卡片的内部没有边距时,会显得很拥挤,给人一种非常窒息的感觉,强烈的压迫感,因为文字没有"呼吸空间",就像下面这样:

    image-20251205160819679

    一号卡片完全没有边距,看起来非常拥挤,就像装不下似得,但实际上内部有很多空间容纳这些文本。三号卡片虽然有了内边距,但是似乎有点用力过猛,导致边距实际内容区域挤压得没有空间了,周围区域又显得很空洞。

    因此,设置一个合理的内边距,在视觉上能给人一种轻松愉快的感觉,不会显得压抑。边距设置一般可以考虑按照4的倍数进行调整,比如如8 / 12 / 16 / 20 / 24 px等。

  3. 卡片的圆角:直角卡片有强烈的"边界感",多个直角卡片并排时会显得密集、紧绷,直角有抢夺内容注意力的副作用(像这类容易抢夺人注意力的内容,一般将其称为"视觉噪音")现代化的UI设计,更多地会考虑使用圆角来让边界自然过度,防止抢夺过多注意力。就像Windows10到Windows11的变化一样,圆角的设计更受用户的喜爱。

    image-20251206000455437

    可以看到,一号卡片由于没有设置圆角,四个尖尖看着就非常难受,有点明明是跑龙套的非要出头秀一波的感觉。三号卡片虽然添加了圆角,但是似乎有点过度圆滑了,看着有点像丸子或气泡的感觉,如果再大一点就会导致圆角处的外边距被彻底磨平,就又出现上面没有边距的压迫感了。

    因此,圆角的大小应该根据盒子的内边距进行合理调整,越大的圆角,就需要越大的内边距来进行匹配,而不是一味地去打磨。一般情况下圆角的半径不建议超过内边距的大小。

正在加载页面,请稍后...