CSS 的视觉格式化模型(Visual Formatting Model)是 CSS 规范中定义的一组规则,用于描述如何在屏幕上渲染 HTML 元素。这个模型规定了元素的大小、位置、边距、内边距、边框以及它们如何相互作用。理解视觉格式化模型对于创建复杂的网页布局至关重要。
视觉格式化模型的关键概念
-
盒子模型(Box Model)
- 每个 HTML 元素都被视为一个矩形盒子,这些盒子可以嵌套在一起形成页面布局。
- 盒子由内容区(content)、内边距(padding)、边框(border)和外边距(margin)组成。
-
盒子类型(Box Types)
- 块级盒子(Block Boxes) :默认情况下,
display: block的元素,如<div>、<p>等,占据整行,形成新的行级盒子。 - 行内盒子(Inline Boxes) :
display: inline的元素,如<span>、<a>等,与其他行内元素在同一行内显示。 - 行内级盒子(Inline-Level Boxes) :包括行内元素和行内块元素(
display: inline-block)。 - 原子行内级盒子(Atomic Inline-Level Boxes) :不可分割的行内元素,如
<img>、<input>等。
- 块级盒子(Block Boxes) :默认情况下,
-
定位方案(Positioning Schemes)
- 普通流(Normal Flow) :元素按照文档流自然排列,没有特别的位置指定。
- 浮动(Floating) :元素脱离普通流,向左或向右浮动,直到遇到边界或其他浮动元素。
- 绝对定位(Absolute Positioning) :元素相对于最近的已定位祖先元素进行定位。
- 相对定位(Relative Positioning) :元素相对于其正常位置进行定位。
- 固定定位(Fixed Positioning) :元素相对于视口进行定位。
- 粘性定位(Sticky Positioning) :元素的行为介于相对定位和固定定位之间。
-
包含块(Containing Block)
- 包含块是决定一个元素如何定位的基础。对于大多数块级元素,包含块是其最近的父元素的边框边缘。对于绝对定位元素,包含块是最近的已定位祖先元素的内容边缘。
-
布局算法(Layout Algorithms)
- 浏览器使用不同的算法来确定元素的位置和大小。例如,块级元素使用块格式化上下文(Block Formatting Context),而行内元素则使用行内格式化上下文(Inline Formatting Context)。
示例
假设我们有以下 HTML 结构:
<div class="parent">
<div class="child">Child Element</div>
</div>
并且有以下 CSS:
.parent {
border: 1px solid black;
padding: 10px;
margin: 20px;
}
.child {
border: 1px solid blue;
padding: 5px;
margin: 5px;
}
根据视觉格式化模型,.parent 元素是一个块级盒子,它有自己的边框、内边距和外边距。.child 元素也是一个块级盒子,位于 .parent 内部,并且有自己的边框、内边距和外边距。.child 元素的包含块是 .parent 的内容边缘。
CSS 规范中的规则定义了如何编写和组织 CSS 代码,以确保代码的可读性和可维护性。这些规则不仅有助于团队协作,还能提高代码的质量。
1. 选择器命名规范
-
命名约定:选择器的命名应该具有描述性,避免使用过于通用的名字(如
selected,current,disabled等)。如果必须使用这些通用名称,应确保它们在一个特定的上下文中使用,以避免冲突。例如,如果有一个通用的类名为
.current,最好在特定的上下文中使用,如#parent .current。 -
自定义命名:除了预定义的命名前缀(如
.fn-,.ui-,.sl-)外,可以自定义命名,但需遵循一致的命名约定。 -
JS 接口命名:作为 JavaScript 接口的选择器或 ID 必须以
J-前缀开始,以区分它们与普通的 CSS 选择器。
2. 文件编码和引入方式
-
文件编码:所有 CSS 文件必须使用 UTF-8 编码(无 BOM),这是为了确保字符编码的一致性和兼容性。
-
引入方式:CSS 文件应通过
<link>标签引入,而不是使用@import。这是因为<link>方式可以并行加载,而@import则会阻塞页面的渲染。例如:
<link rel="stylesheet" href="styles.css"> -
内联样式:仅当 CSS 代码只在单个页面中使用时,才应该将其写入
<style>标签中。通常推荐将样式提取到外部文件中,以提高复用性和缓存效率。
3. 注释
-
组件注释:每个组件都应该有注释说明其用途和功能,以便其他开发者了解其目的。
例如:
/* .header-component: Header component for the application. Includes logo and navigation links. */ .header-component { /* styles here */ }
4. 选择器优先级
-
选择器权重:理解选择器的权重(specificity)是非常重要的,因为这决定了样式规则的优先级。具体来说,ID 选择器 > 类选择器 > 标签选择器 > 通用选择器。
例如:
#id .class p { color: red; /* 更具体的规则 */ } p { color: blue; /* 较不具体的规则 */ }在这个例子中,
#id .class p的优先级高于p,因此文本颜色将是红色。
5. 响应式设计
-
媒体查询:使用媒体查询来适应不同设备和屏幕尺寸,确保网站在各种设备上都有良好的表现。
例如:
@media (max-width: 600px) { .header-component { font-size: 14px; } }
6. 维护性和可扩展性
- 模块化:将 CSS 分为多个模块,每个模块负责一部分功能,这样可以更容易地管理和扩展。
- 重用:尽量减少重复的样式规则,通过组合选择器或使用预处理器(如 Sass)中的混合(mixins)来重用样式。
7. 命名空间
- 命名空间:为了避免全局命名冲突,可以使用命名空间。例如,为特定组件添加前缀,如
.my-app-header。
8. BEM 方法 (规范)
-
Block Element Modifier (BEM) :这是一种流行的命名约定,用于组织 CSS 类名。它鼓励使用有意义的类名,并通过特定的结构来表示组件的不同部分和状态。
例如:
.block {} /* 块 */ .block__element {} /* 元素 */ .block--modifier {} /* 修改器 */BEM(Block Element Modifier)是一种流行的 CSS 命名方法论,最初由 Yandex 团队提出。BEM 的核心思想是将用户界面(UI)分解成独立的、可复用的组件(称为“块”),并通过明确的命名约定来定义这些组件及其内部结构。这种命名方法论旨在提高 CSS 代码的可维护性和可重用性。
BEM 的组成部分
BEM 命名规范主要由三个部分组成:
-
Block(块) :块是页面上的独立部分,可以被重用。每个块都代表了一个抽象的 UI 组件或接口的一部分,如表单、导航栏、按钮等。
例如:
.header {} .navigation {} .button {} -
Element(元素) :元素是块的一个部分,不能单独存在。元素总是属于某个块,并且它的样式依赖于该块的存在。
例如:
css .header__logo {} .navigation__item {} .button__text {} -
Modifier(修饰符) :修饰符改变块或元素的外观或行为。修饰符通常用于表示不同的状态或变体。
例如:
css .button--primary {} .button--secondary {} .navigation--horizontal {}
BEM 的命名规则
BEM 的命名规则遵循一定的格式,使得类名具有描述性和可预测性。基本的命名格式如下:
- 块:
block - 元素:
block__element - 修饰符:
block--modifier或block__element--modifier
BEM 的优点
- 可维护性:由于 BEM 的命名约定非常明确,它使得 CSS 代码更容易理解和维护。每个类名都清楚地表明了它所代表的 UI 组件的部分和功能。
- 可重用性:BEM 鼓励将 UI 组件设计为独立的块,这些块可以在不同的页面或应用中重用。
- 隔离性:通过使用双下划线(
__)和双连字符(--)来区分元素和修饰符,BEM 有助于避免样式污染,即一个组件的样式不会意外影响到另一个组件。 - 团队协作:BEM 的命名约定有助于团队成员之间达成一致,减少了沟通成本,提高了开发效率。
BEM 的使用场景
BEM 最适合用于大型项目或需要多人协作的项目中。它尤其适用于那些需要高度可维护性和可重用性的应用。对于小型项目或个人项目,是否使用 BEM 可以根据具体情况来决定,因为在这种情况下,BEM 的命名可能会显得有些冗长(更多体现都是组件库的css命名、第三方库等)
CSS 中的换行规则
1. 默认换行行为
在默认情况下,文本会在达到容器宽度时自动换行。这种行为适用于大多数的文本内容,特别是在英文和其他西文语言中。
2. white-space 属性
white-space 属性控制如何处理元素中的空白字符(如空格、制表符等)以及换行行为:
normal:这是默认值,会折叠所有的空白字符,并且在内容到达边界时换行。pre:保留所有的空白字符,包括空格和换行符,不自动换行。nowrap:不折叠空白字符,并且不允许换行,即使内容超出了容器的宽度。pre-wrap:保留空白字符,但在必要时会换行。pre-line:保留换行符,但折叠空格,并在必要时换行。
3. word-break 属性
word-break 属性指定了如何在单词内部断行:
normal:使用默认的断行规则,不拆分单词。break-all:对于非CJK(中文/日文/韩文)文本,可以在任意字符间断行。keep-all:对于CJK文本,不在单词中间断行;对于非CJK文本,表现同normal。break-word:在单词太长以至于超过了容器宽度时,会尝试在单词内部断行。
4. overflow-wrap 属性
overflow-wrap 属性类似于 word-break,但它更侧重于溢出内容的处理:
normal:使用默认的断行规则,不拆分单词。break-word:当内容超出容器宽度时,在单词内部断行。anywhere:允许在任何地方断行,类似于word-break: break-all。
5. hyphens 属性
hyphens 属性用于控制是否在单词之间自动添加连字符:
none:不自动添加连字符。manual:手动添加连字符。auto:自动在单词之间添加连字符。
6. text-overflow 属性
虽然不是直接控制换行,但 text-overflow 属性可以用来处理超出容器的文本:
clip:剪切超出容器的文本。ellipsis:用省略号(...)替换超出容器的文本。string:使用指定的字符串替换超出容器的文本。
7. line-break 属性
line-break 属性控制文本换行的时机,尤其是在东亚语言中:
auto:使用浏览器默认的换行策略。loose:允许更多的换行,即使是在单词中间。strict:尽可能少地换行,避免在单词中间断行。normal:介于loose和strict之间。
示例
假设我们有一个包含长单词的段落,并希望在单词太长时自动断行,可以使用以下 CSS:
p {
word-break: break-word;
overflow-wrap: break-word;
hyphens: auto;
}
这段代码会使得段落中的单词在超出容器宽度时自动断行,并且在适当的地方自动添加连字符。
继承规则(Inheritance)
继承是指某些 CSS 属性可以被子元素自动继承自父元素。这意味着如果没有为子元素指定某个属性的值,那么子元素就会采用父元素的该属性值。继承可以减少重复定义相同的属性值,使样式表更加简洁。
可继承的属性示例
- 字体相关属性:如
font-family、font-size、font-style、font-weight等。 - 颜色属性:如
color。 - 文本对齐方式:如
text-align。 - 列表样式:如
list-style。
不可继承的属性示例
- 边框:如
border。 - 背景:如
background。 - 盒模型:如
padding、margin、width、height。 - 定位:如
position。
示例
body {
font-family: Arial, sans-serif;
color: #333;
}
p {
/* 继承 body 的 font-family 和 color */
/* 如果不设置,则会使用 body 的值 */
}
h1 {
/* 继承 body 的 color */
font-size: 2em;
}
层叠规则(Cascading)
层叠是指当多个样式规则应用到同一个元素时,浏览器如何决定哪些规则应该被应用。CSS 的名字本身就有“层叠样式表”的意思,这意味着样式可以叠加,并且某些样式可以覆盖其他的样式。
层叠规则的影响因素
- 源顺序:样式表中的规则按照它们在文档中的出现顺序应用。
- 特异性(Specificity) :选择器的特异性越高,其优先级越高。特异性从高到低依次为:ID 选择器、类选择器、属性选择器、类型选择器和通用选择器。
- 重要性(!important) :可以在声明中使用
!important来提高规则的优先级,但应谨慎使用。 - 继承:继承来的规则没有特殊性,除非它们是通过继承得到的。
示例
/* 特异性为 0,0,0,1 */
p {
color: red;
}
/* 特异性为 0,0,1,0 */
p.special {
color: blue;
}
/* 特异性为 1,0,0,0 */
#example p {
color: green;
}
/* 特异性为 0,0,0,1 + !important */
p {
color: yellow !important;
}
在这个例子中,即使 p 选择器后面跟有 !important,它仍然会覆盖前面的规则,因为 !important 的优先级最高。然而,如果有一个更高特异性的规则也带有 !important,那么它将会覆盖带有 !important 的较低特异性规则。
CSS 计算规则涉及到了 CSS 样式的解析、选择器的匹配、样式的继承与层叠,以及最终样式的计算与应用。CSS 的计算规则是一个复杂的过程,它确保了每个元素的所有样式属性都被正确地计算出来,从而决定了元素在页面上的最终呈现方式。下面是对 CSS 计算规则的详细解释:
CSS计算规则
1. 样式声明的收集
在计算 CSS 样式之前,浏览器需要收集所有可能应用于某个元素的样式声明。这些样式声明可能来自多个来源,包括:
- 作者样式表:由开发者编写的 CSS 样式表。
- 浏览器默认样式表:浏览器内置的默认样式,这些样式在开发者未指定任何样式时生效。
- 用户样式表:用户自定义的样式表,可以覆盖作者样式表中的某些规则。
2. 选择器的匹配
浏览器会对每个元素应用所有可能的样式声明,并根据选择器的匹配规则来决定哪些样式声明适用于该元素。选择器的匹配过程遵循 CSS 规范中定义的规则,包括特异性(specificity)和层叠顺序(cascade order)。
特异性(Specificity)
特异性是指选择器的优先级,它决定了当多个样式声明同时应用于同一元素时,哪一个声明会被应用。特异性由选择器的构成决定,按从高到低排序如下:
- ID 选择器(100)
- 类选择器、属性选择器、伪类(10)
- 标签选择器、伪元素(1)
- 通用选择器、子选择器、相邻兄弟选择器(0)
层叠顺序(Cascade Order)
当两个或更多具有相同特异性的选择器同时应用于一个元素时,层叠顺序决定了哪个样式声明会被应用。层叠顺序考虑的因素包括:
- 源顺序:样式表中的规则按照它们在文档中的出现顺序应用。
- 重要性(!important) :带有
!important的声明优先级更高。 - 用户代理样式表 vs. 用户样式表 vs. 作者样式表:用户代理样式表(浏览器默认样式)< 用户样式表 < 作者样式表。
3. 样式的计算
一旦确定了哪些样式声明适用于某个元素,浏览器就开始计算这些样式的最终值。计算过程包括:
- 确定初始值:如果一个属性没有被任何样式声明定义,它将使用 CSS 规范中定义的初始值。
- 继承:某些属性可以从父元素继承。如果一个元素的样式声明没有定义某个属性,它将继承父元素的该属性值。
- 计算值:对于某些属性,浏览器需要将预设值(如
inherit、initial、unset)转换为实际的计算值。例如,颜色值currentColor实际上是指元素的color属性值。
4. 样式的应用
在计算完所有样式的最终值后,浏览器将这些样式应用到元素上,从而决定了元素在页面上的最终呈现效果。此过程中,浏览器还需要考虑其他因素,如布局(layout)和渲染树(render tree)的构建。
示例
假设我们有一个简单的 HTML 结构:
<div id="main">
<p class="text">Hello, world!</p>
</div>
对应的 CSS 样式可能如下:
/* 浏览器默认样式 */
p {
color: black;
}
/* 作者样式 */
#main p {
color: red;
}
.text {
color: blue;
}
/* 用户样式 */
p {
color: green;
}
在这个例子中,p 元素的颜色将被计算为 blue,因为 .text 类选择器具有更高的特异性(10),并且在层叠顺序中优先于其他规则。
总结
CSS 的计算规则是一个多层次的过程,涉及到选择器的匹配、特异性的计算、层叠顺序的确定以及样式的继承。理解这些规则有助于开发者更好地控制页面元素的样式,确保样式按照预期的效果呈现。
结论
CSS 的视觉格式化模型(Visual Formatting Model)是 CSS 规范中定义的一组规则,这些规则决定了文档中的元素是如何被渲染成可视化的布局。这个模型描述了如何根据元素的类型、CSS 属性、元素之间的关系等因素来排列和定位元素。
分享心得
每次梳理知识点 再小的点面深入一些都很吃惊,背后很多概念很多流程很多巧妙的思想都能让你一个写了好多年代码的程序员自认为没问题的人难倒~尤其是众人口中难学的CSS。。。