务必了解的 CSS 世界的专业术语

本章节主要涉及CSS属性、值、关键字、变量、选择器等,在此不多做赘述,仅讲解部分特殊内容。

未定义行为

CSS直接中有web的标准来约束元素的行为,但是标准无法面面俱到,所以标准未规定的场景只能由各大浏览器厂家自行实现,这些场景就是未定义行为。具体表现为在部分场景下,不同浏览器的表现不一致。

流、元素与基本尺寸

按照 W3C 的 CSS 规范区分,这里应该分为“块级元素”和“内联级元素” (inline-level element)。但是,在 W3C 的 HTML4 规范中,已经明确把 HTML 元素分成了 “块级元素”和“内联元素”,后续”内联级元素“和”内联元素“都将以”内联元素“称呼。

块级元素

常见的块级元素有<div><li><table>

注意,块级元素并不等于display: block的元素。例如,<li>displaylist-item<table>displaytable

“内外盒子”

list-item

最开始,只有块级盒子内联盒子,但是这个模型无法解释list-item元素为什么会出现项目符号。

所以,添加了一个标记盒子,用于放置圆点、数字等项目符号,同时为了兼容上面的描述,还需要有一个主块级盒子

未标题-1

inline-block

这个标签同时具有inline和block的两种特性,上述的模型也无法解释。为了便于理解,我们可以认为每个元素都有至少两个盒子,外在盒子和内在盒子。外在盒子负责流动性的管理(一行显示、换行显示),内在盒子负责元素的宽高、内容呈现。

我们将内在盒子换成更专业的名称——容器盒子

举个例子:

  • display: block的元素是由外在的块级盒子内在的块级容器盒子构成的。
  • display: inline-block的元素是由外在的内联盒子内在的块级容器盒子构成的。
  • display: inline的元素是由外在的内联盒子内在的内联容器盒子构成的。

width和height作用在哪里?

width

width的初值:auto

在本菜鸟编写样式代码中,经常会出现宽度不符合预期的情况,很多时候并不知道为什么,只能加些属性调试调试,既浪费时间,也增加了不少的冗余代码,下面我们将讨论width的auto值。

auto至少包含了四种不同的宽度表现:

  1. 充分利用空间,比如<div><p>等,表现为width: 100%,叫做fill-available
  2. 收缩与包裹,比如浮动、绝对定位、inline-block、table元素等,叫做shrink-to-fit,与CSS3中的fit-content一致;
  3. 收缩到最小,比如table-layout为auto的表格中,叫做minimum content width,与CSS3中的min-content一致;
  4. 超出容器限制,比如很长的连续英文或数字,或设置了white-space: nowrap,与CSS3中的max-content一致;

image-20231017155853132

1
2
3
4
5
6
7
8
9
10
11
<div style="width: 200px">
<p>这是一个P标签</p>
<p style="white-space: nowrap">这是一个设置了禁止换行的很长很长很长很长很长很长很长很长很长很长很长很长很长的P标签</p>
<div style="position: absolute">这是一个浮动的div标签</div>
</div>

<style>
div, p {
border: #1a1a1a solid 1px;
}
</style>
外部尺寸与流体特性

上述的auto中的第一条充分利用空间就是外部尺寸

了解了这些后,我们可以尽量避免代码中的width的显式设置,利用流的特性来实现样式的排布,适配性更高且代码更好维护。

在页面中随便扔一个元素,其尺寸表现就会和这水流 一样铺满容器。这就是 block 容器的流特性。这种特性,所有浏览器的表现都是一致的。

image-20231017160915329

内部尺寸与流体特性

上述的auto中的第二、三、四条都是内部尺寸,下面将一一对应的介绍:

  1. 包裹性:

​ 这里用<button>标签来理解:

1
2
3
4
5
6
7
8
9
<div class="box">
<button>按钮</button>
</div>
<style>
.box {
width: 240px;
margin: 20px auto;
}
</style>

image-20231017161846449

可以看到,当宽度小于父容器时,按钮的宽度会包裹住内容。

当宽度大于父容器时,按钮会自动换行,并不会超出父容器的宽度。

在开发中有以下场景可以用到这个特性:

image-20231017162321956

此时只需:

1
2
3
4
5
6
7
.box {
text-align: center;
}
.content {
display: inline-block;
text-align: left;
}
  1. 首选最小宽度:

元素的最适合的最小宽度。

  • 东亚文字(如中文)的最小宽度为每个汉字的宽度;
  • 西亚文字最小宽度由特定的连续的英文字符单元决定;具体可以使用white-space、word-break等属性进行设置;
  • 类似图片这样的替换元素的最小宽度就是该元素内容本身的宽度;
  1. 最大宽度:

最大宽度就是元素可以有的最大宽度。如果内部没有块级元素或者块级元素没有设定宽度值,则“最大宽度”实际上是最大的连 续内联盒子的宽度。

image-20231017185308986

细节

上面我们讨论到元素的宽高是由容器盒子决定的,而容器盒子的结果就是著名的盒模型

image-20231017190639474

里面对应content-box、padding-box、border-box和margin-box(?)。

但是实际上并没有margin-box,因为没有任何需要用到的场景。margin的背景永远是透明的

基于以上的前置知识,我们可以推测出,width是作用在content box上的。这样的设定可能带来以下的问题:

  1. 流动性丢失:如果 width: auto,则元素会如水流般充满整个容器,而一旦设定了 width 具体数值,则元素的流动性就会被阻断。

image-20231017191652868

  1. 使用起来有一定障碍,下面将介绍“宽度分离原则”

    不能出现以下的组合:

    1
    2
    3
    .box { width: 100px; border: 1px solid; } 

    .box { width: 100px; padding: 20px; }

    应将width独立占用一层标签,而padding、border、margin 利用流动性在内部自适应呈现。

    1
    2
    3
    4
    5
    6
    7
    8
    .father { 
    width: 180px;
    }
    .son {
    margin: 0 20px;
    padding: 20px;
    border: 1px solid;
    }

    更加便于维护,代码可读性更强。但也增加新的html节点,有一定的成本。

height

height:autowidth:auto简单而单纯的多。因此,宽度的分配规则就比较复杂,高度的就显得比较随意。

height:auto的表现基本上就是有几个元素盒子,每个多高,累加得到最终值。

当然其他的场景比如说float或者margin等,下面也会具体讨论。

height: 100%

对于height属性,如果父元素的height为auto,只要子元素在文档流中,其百分比值就被完全忽略了。

比如一个很常见的错误写法:

1
2
3
4
5
div { 
width: 100%; /* 这是多余的 */
height: 100%; /* 这是无效的 */
background: url(bg.jpg);
}

结果就是这个div的高度永远是0,即使父级<body>已经塞满了内容,想实现的话还需要设置:

1
2
3
html, body {
height: 100%;
}

对于普通文档流中的元素,百分比高度值要想起作用, 其父级必须有一个可以生效的高度值

1. 为何height: 100%无效?

首先,我们要了解浏览器渲染的基本原理:先下载文档内容,加载头部的 样式资源(如果有的话),然后按照从上而下、自外而内的顺序渲染 DOM 内容。

体现在上面的例子就是:先渲染父元素,后渲染子元素,是有先后顺序的。

因此,当渲染到父元素的时候,子元素的 width:100%并没有渲染,宽度就是图片加文字内容的宽度;等渲染到文字这个子元素的时候, 父元素宽度已经固定,此时的 width:100%就是已经固定好的父元素的宽度。宽度不够怎么办?溢出就好了,overflow 属性就是为此而生的。

高度和宽度的原理类似,但是在规范中,高度的定义是:

如果包含块的高度没有显式指定(即高度由内容决定),并且该元素不是绝对定位,则计算值为 auto。

一句话总结就是:因为解释成了 auto。对于auto进行百分比计算式没有意义的。

宽度的定义是:如果包含块的宽度取决于该元素的宽度,那么产生的布局在 CSS 2.1 中是未定义的。

经过测试,绝大多数浏览器的width计算并不像高度一样变成auto,所以对它的宽度设置百分比是有效的。

2. 如何让元素支持height:100%效果
  1. 设置显式的高度值:

    1
    2
    3
    html, body { 
    height: 100%;
    }
  2. 使用绝对定位:

    1
    2
    3
    4
    div {
    height: 100%;
    position: absolute;
    }

CSS min-width/max-width和min-height/max-height

这些属性和我们上面讨论的width和height在盒尺寸机制和一些值的渲染规则上是一致的,在此我们不多做讨论。我们主要讨论这几个属性和width和height不一样的地方。

为流体而生的 min-width/max-width

在 CSS 世界中,min-width/max-width 出现的场景一定是自适应布局或者流体布局中。考虑以下两个场景:

  1. 现代桌面显示器分辨率越来越大,960 像素网页设计已经显得有些小家碧玉了,但随便搞 一个大尺寸(如 1400 像素)的网页宽度也不合时宜,因为还有很多笔记本用户,此时,一种特 定区间内的自适应布局方案就诞生了,网页宽度在 1200~1400 像素自适应,既满足大屏的大气, 又满足笔记本的良好显示,此时,min-width/max-width 就可以大显神威了:

    1
    2
    3
    4
    .container {
    min-width: 1200px;
    max-width: 1400px;
    }
  2. 在公众号的热门文章中,经常会有图片,这些图片都是用户上传产生的,因此尺寸会有大 有小,为了避免图片在移动端展示过大影响体验,常常会有下面的 max-width 限制:

    1
    2
    3
    4
    img {
    max-width: 100%;
    height: auto!important;
    }

    height: auto是必需的,否则,如果原始图片有设定height,max-width 生效的时候, 图片就会被水平压缩。强制height 为 auto 可以确保宽度不超出的同时使图片保持原来的比 例。但这样也会有体验上的问题,那就是在加载时图片占据高度会从 0 变成计算高度,图文会有明显的瀑布式下落。

与众不同的初始值

min-width/max-width 和 min-height/max-height 的初始值则要复杂些。这里要分为两部分,分别是 max-*系列和 min-*系列。 max-width 和 max-height 的初始值是none,min-width 和 min-height 的初始值是auto。

超越!important,超越最大

max-width会覆盖width,而且这种覆盖不是普通的覆盖,它的优先级甚至高于!important。

超越最大指的是min-width覆盖max-width,此规则发生在min-width和max-width 冲突的时候。

当最小宽度比最大宽度设置得还大时,遵循 “超越最大”规则(注意不是“后来居上”规则),min-width 活下来,max-width 被忽略。

内联元素

如何辨别内联元素

  1. 从定义看:”内联元素“的”内联“特指”外在盒子“,和display为inline不是一个概念;
  2. 从表现看:“内联元素”的典型特征就是可以和文字在一行显示。因此,文字是内联元素,图片是内联元素,按钮是内联元素,输入框、下拉框等原生表单控件也是内联元素。

内联盒模型

下面是一段很普通的 HTML:

1
<p>这是一行普通的文字,这里有个 <em>em</em> 标签。</p>

但是这段HTML中包含了很多种盒子,主要包括:

  1. 内容区域:内容区域指一种围绕文字看不见的盒子,其大小仅受字符本身 特性控制,本质上是一个字符盒子(character box);但是有些元素,如图片这样的替换元素,其内 容显然不是文字,不存在字符盒子之类的,因此,对于这些元素,内容区域可以看成元素自身。

  2. 内联盒子:“内联盒子”不会让内容成块显示,而是排成一行,这里的“内联盒子”实际指的就是元素的“外在盒子”,用来决定元素是内联还是块级。该盒子又可以细分 为“内联盒子”和“匿名内联盒子”两类:

    image-20231025105119672如果外部含内联标签(<span><a><em>等),则属于“内联盒子”(实线框标注);如 果是个光秃秃的文字,则属于“匿名内联盒子”(虚线框标注)。 需要注意的是,并不是所有光秃秃的文字都是“匿名内联盒子”,其还有可能是“匿名块 级盒子”,关键要看前后的标签是内联还是块级。

  3. 行框盒子: image-20231025105819185

    每一行就是一个“行框盒子”(实线框标注),每个“行框盒子”又是由一个一个“内联盒子” 组成的。

  4. 包含盒子:

    image-20231025105857640

    <p>标签就是一个“包含盒子”,此盒子由一行一行的“行框盒子”组成。

    需要补充说明一点,在 CSS 规范中,并没有“包含盒子”的说法,更准确的称呼应该是“包 含块”(containing block)。这里之所以把它称为盒子,一是为了与其他盒子名称统一,二是称 为盒子更形象、更容易理解。

幽灵空白节点

“幽灵空白节点”是内联盒模型中非常重要的一个概念,具体指的是:在 HTML5 文档声明 中,内联元素的所有解析和渲染表现就如同每个行框盒子的前面有一个“空白节点”一样。这 个“空白节点”永远透明,不占据任何宽度,看不见也无法通过脚本获取,就好像幽灵一样, 但又确确实实地存在,表现如同文本节点一样。

1
2
3
4
5
6
7
8
9
<!DOCTYPE html>
<html>
<body>
<div style="background-color: red;">
<span style="display: inline-block;">
</span>
</div>
</body>
</html>

image-20231025110903104

虽然说“幽灵空白节点”是我自己根据 CSS 的特性表现起的一个非常形象的名字,但其绝不 是空中楼阁、信口胡诌的。规范中实际上对这个“幽灵空白节点”是有提及的,“幽灵空白节点” 实际上也是一个盒子,不过是个假想盒,名叫“strut”,中文直译为“支柱”,是一个存在于每个“行 框盒子”前面,同时具有该元素的字体和行高属性的 0 宽度的内联盒。