盒子模型(Box Model)

所有HTML元素可以看作盒子,在CSS中,"box model"这一术语是用来设计和布局时使用。

CSS盒模型本质上是一个盒子,封装周围的HTML元素,它包括:边距,边框,填充,和实际内容。

盒模型允许我们在其它元素和周围元素边框之间的空间放置元素。

盒子模型的结构
盒子模型的结构

盒模型的属性:

margin:元素的外边距

border:元素的边框

padding:元素的内边距

content:元素的内容

width:元素的宽度

height:元素的高度

外部显示类型

Block 显示类型

Block 类型的盒子具有以下特征:

  • 会在新行开始显示,并且占据一整行
  • 可以设置 width 和 height 属性
  • padding、margin 和 border 会推开其他元素
  • 如果不设置宽度,会自动填充父容器的宽度(100%)
  • 常见的块级元素:<div><h1>-<h6><p><section>

Inline 显示类型

Inline 类型的盒子具有以下特征:

  • 在同一行内显示,不会强制换行
  • width 和 height 设置无效
  • 垂直方向的 padding、margin 和 border 虽然可见,但不会影响其他元素的位置
  • 水平方向的 padding、margin 和 border 会推开周围元素
  • 常见的行内元素:<span><a><strong><em>

可以通过 display 属性来改变元素的显示类型,如 display: blockdisplay: inline

内部显示类型

除了外部显示类型,每个盒子还具有内部显示类型,它决定了盒子内部子元素的排列和布局方式。

在默认情况下,HTML元素会遵循标准文档流进行布局,表现为块级(block)或行内(inline)盒子。这是最基本的布局行为,不需要额外的CSS设置。

我们可以通过修改display属性来改变元素的内部显示类型。一个典型的例子是设置display: flex,这会使该元素在保持块级(block)外部显示类型的同时,将内部显示类型变为弹性布局(flex)。此时,该元素的所有直接子元素都会成为弹性项(flex items),并按照弹性盒子(Flexbox)的规则进行排列和布局。

替代盒模型

替代盒模型 (box-sizing: border-box) 的核心概念是:

你设置的 width 和 height 就是这个盒子最终的、肉眼可见的总尺寸,不多也不少。

它将 padding (内边距) 和 border (边框) 的厚度全部包含在你设定的 width 和 height 之内。

这意味着,你设置的宽度和高度,就是元素内容区域的宽度和高度。

而不是包括内边距和边框的宽度和高度。

这与标准盒模型 (box-sizing: content-box) 形成对比,后者将宽度和高度定义为内容区域的尺寸。

替代盒模型的优势在于,它更符合直觉,更方便进行布局和尺寸调整。

例如,当你设置一个元素的宽度为 200px,高度为 100px,并且同时设置了 20px 的内边距和 5px 的边框时,

替代盒模型会将这 20px 的内边距和 5px 的边框包含在 200px 和 100px 之内,而标准盒模型会将这 20px 的内边距和 5px 的边框添加到 200px 和 100px 之外。

这意味着,你可以更方便地控制元素的尺寸,而不需要考虑内边距和边框的影响。

标准盒模型盒替代盒模型的区别

在标准盒模型 (content-box) 中,你设置的 width 和 height 属性,只直接控制 content (内容) 区域的尺寸。

在替代盒模型 (border-box) 中,padding 和 border 的改变,会反过来影响 content 区域的大小。

  1. 标准盒模型 (content-box)

width 属性控制谁?

只控制 content 的宽度。

padding 和 border 的作用?

在 width 之外继续增加尺寸,导致整个盒子的最终视觉尺寸变大。

content 的宽度如何决定?

直接由你设置的 width 属性决定。它是固定不变的。

比喻: width 是你箱子里能装东西的空间。padding(泡沫)和border(纸板)是在这个空间之外额外加上去的厚度。

  1. 替代盒模型 (border-box)

width 属性控制谁?

控制整个盒子(从左边框外缘到右边框外缘)的最终视觉宽度。

padding 和 border 的作用?

在 width 之内向内挤占空间。它们的增加,会导致 content 区域自动变小,但盒子的总尺寸保持不变。

content 的宽度如何决定?

由 width 减去 padding 和 border 的厚度之后剩下的空间决定。它是动态计算的。

比喻: width 是你整个房间的宽度(从墙的一边到另一边)。padding(墙边留空)和border(墙壁厚度)占用的空间,是从这个总宽度里扣除的,剩下的才是 content(给你放家具的空间)。

display: inline-block

display: inline-block 是 display 的一个特殊值,它提供了介于 inline 和 block 之间的中间位置。如果不希望项目换行,但又希望它使用 width 和 height 值并避免出现上述重叠现象,请使用它。

一个元素使用 display: inline-block,实现我们需要的块级的部分效果:

·设置 width 和height 属性会生效。

·padding、margin 和 border 会推开其他元素。

不过,它不会换行,只有在明确添加 width 和 height 属性后,才会变得比其内容大。

层叠、继承、优先级

冲突规则

CSS 代表层叠样式表(Cascading Style Sheets),理解第一个词层叠(cascade)很重要——层叠的表现方式是理解 CSS 的关键。

在某些时候,在做一个项目过程中你会发现一些应该产生效果的样式没有生效。通常的原因是你创建了两个应用于同一个元素的规则。与层叠密切相关的概念是优先级(specificity),决定在发生冲突的时候应该使用哪条规则。设计元素样式的规则可能不是期望的规则,因此需要了解这些机制是如何工作的。

这里也有继承的概念,也就是在默认情况下,一些 css 属性继承当前元素的父元素上设置的值,有些则不继承。这也可能导致一些和期望不同的结果。

层叠

在下面的例子中,h1 元素的颜色是蓝色,而不是红色。这是因为第二个规则层叠了第一个规则。

什么是层叠,按我的理解就是顺序执行,执行到最后一条规则时就会把之前的规则覆盖掉

在下面的例子中就是,第二个h1元素的颜色是蓝色,第一个h1元素的颜色被第二个h1元素的颜色覆盖了

<h1>这是我的标题。</h1>

h1 {

  color: red;

}

h1 {

  color: blue;

}

优先级

在层叠的例子中,虽然是顺序执行的,蓝色将红色覆盖掉了。所以蓝色的规则优先级更高。

什么是优先级,按我的理解就是,当有多个规则应用到同一个元素上时,浏览器会根据优先级来确定哪个规则生效。

优先级的计算规则是,先计算选择器的specificity(具体性),具体性高的规则生效。

如果具体性相同,那么后面的规则生效。

<h1 class="title">这是我的标题。</h1>

.title {
    color: red;
}

h1 {

  color: red;

}

h1 {

  color: blue;

}

很明显在上边的例子中,.title 类选择器的优先级更高,所以颜色是红色。

继承

<body>
<p>由于主题颜色被设置为蓝色,因此该颜色会被子元素继承。</p>
<p>我们可以通过选择器定位元素来改变颜色,比如这个<span>内容跨越</span>元素。</p>

<style>
body {
  color: blue;
}

span {
  color: black;
}
</style>
</body>

在上面的例子中,p 元素的颜色是蓝色,span 元素的颜色是黑色。这是因为 p 元素是 body 元素的子元素,所以会继承 body 元素的颜色。

注意:继承的属性是可以被覆盖的,比如在上面的例子中,我们给 span 元素添加了 color: black; 样式,所以 span 元素的颜色是黑色。

一些属性是不能继承的——举个例子如果你在一个元素上设置 width 为 50% ,所有的后代不会是父元素的宽度的 50%。

重设所有属性值

可以使用 all: unset; 来重设所有属性值。

<body>
<blockquote>
  <p>当前块引用设置了样式</p>
</blockquote>

<blockquote class="fix-this">
  <p>当前块引用未设置样式</p>
</blockquote>

<style>
blockquote {
  background-color: orange;
  border: 2px solid blue;
}

.fix-this {
  all: unset;
}
</style>
</body>

注意:all: unset; 会重设所有属性值,包括继承的属性。

CSS 的简写属性 all 可以用于同时将这些继承值中的一个应用于(几乎)所有属性。它的值可以是其中任意一个(inherit、initial、unset 或 revert)。这是一种撤销对样式所做更改的简便方法,以便回到之前已知的起点。

inherit:继承父元素的属性值。

initial:将属性值设置为其默认值。

unset:如果属性是继承的,则与 inherit 相同;否则与 initial 相同。

revert:将属性值恢复为用户代理的默认样式。

!important

有一个特殊的 CSS 可以用来覆盖所有上面所有优先级计算,不过需要很小心的使用——!important。用于修改特定属性的值,能够覆盖普通规则的层叠。

示例

<p class="better">这是个段落。</p>
<p class="better" id="winning">一个选择器掌管一切!</p>

#winning {
  background-color: red;
  border: 1px solid black;
}

.better {
  background-color: gray;
  border: none !important;
}

p {
  background-color: blue;
  color: white;
  padding: 5px;
}

注意:!important 应该被视为最后的手段,因为它会使代码变得非常复杂,并且会使调试变得更加困难。

1.你会发现第三个规则 color 和 padding 的值被应用了,但是 background-color 没有。为什么?应该三个都应用,因为顺序规则是后面覆盖前面。

2.无论如何,上面的规则赢了,因为类选择器比元素选择器有更高的优先级。

3.两个元素都有 better class,但是第二个有 id 。因为 ID 选择器比类选择器优先级更高(一个页面只能有一个独特的 ID,但是很多元素都有相同的类——ID 对于目标非常独特),红色背景和 1px 黑色边框应该都被应用到第二个元素,第一个元素应该是灰色背景和 no border,根据类选择器。

4.第二个元素有红色背景但是没有边框。为什么?因为 !important 声明在第二条规则里——在 border: none 后面,说明即使计算优先级低,这个属性也使用这个值。

备注:覆盖 !important 唯一的办法就是另一个 !important 具有相同优先级而且顺序靠后,或者更高优先级。

在一种情况下,你可能不得不使用它:当你不能编辑核心的 CSS 模块,不能用任何其他方式覆盖,而你又真的想要覆盖一个样式时。但说真的,如果可以避免的话就不要用它。

级联层

/* 1. 用 @layer 语句预先定义好所有层的顺序,后面的优先级更高 */
@layer reset, base, components, utilities;

/* 2. 把规则放进不同的层 */

@layer base {
  /* 这是一个特指度很高的 ID 选择器,但它在 base 这个低优先级的层里 */
  button#promo-button {
    color: blue; /* 蓝色 */
    font-weight: bold;
  }
}

@layer utilities {
  /* 这是一个特指度很低的 class 选择器,但它在 utilities 这个高优先级的层里 */
  .text-red {
    color: red; /* 红色 */
  }
}
<button id="promo-button" class="text-red">这是一个按钮</button>

最终结果: 这个按钮的文字是 红色 的!

为什么?

虽然 #promo-button (ID选择器) 的特指度远远高于 .text-red (类选择器),但是 .text-red 所在的 utilities 层是在 base 层之后定义的,所以 utilities 层的层级优先级更高。在新的规则下,层级优先级首先被比较,既然 utilities 层赢了,浏览器就不再需要去比较特指度了。

级联层的作用就是,事先定义好层的顺序。然后去使用