从零开始理解 CSS:让网页“活”起来的语言2

CSS笔记小节

前言

之前的CSS笔记整理的有些草率,这里重新仔细的整理一部分笔记,重新理解一下CSS的三种文本存在格式:行内(inline)、内联(internal/embedded)、外部(external)。也顺便把选择器梳理一下。

重新看行内(inline)、内联(internal/embedded)、外部(external)

首先,搞清楚定义:

  • 行内样式(inline style) :指直接写在 HTML 元素上的 style 属性,例如 <div style="color:red">
  • 内联样式 / 嵌入式样式(internal / embedded) :指放在 HTML 文档 <head> 中的 <style>...</style> 块内的样式,作用于该 HTML 文档。
  • 外部样式表(external stylesheet) :指放在单独 .css 文件并通过 <link rel="stylesheet" href="..."> 引入的样式。

在代码中,我们直接的体现如下所示

行内样式(inline)

html 复制代码
<!-- 直接在元素上写 style -->
<button style="background:#007bff; color:white; padding:8px 12px;">
  提交
</button>

内联/嵌入式样式(internal)

html 复制代码
<head>
  <style>
    .btn { background: #007bff; color: white; padding: 8px 12px; }
    @media (max-width: 600px) { .btn { padding: 10px; } }
  </style>
</head>
<body>
  <button class="btn">提交</button>
</body>

外部样式表(external)

html 复制代码
<!-- index.html -->
<head>
  <link rel="stylesheet" href="/styles/main.css">
</head>
<body>
  <button class="btn">提交</button>
</body>
/* /styles/main.css */
.btn {
  background: #007bff;
  color: white;
  padding: 8px 12px;
}

优先级的处理问题

CSS的灵活很容易导致一个严肃的问题。大型工程中,如果出现了实际上重复样式渲染的选择,我们如何裁决。这里,CSS的规则如下

  1. !important 声明会提升优先级(先比较 !important,再比较普通声明)。
  2. 同为普通声明时,内联样式(style 属性) 的特异性非常高,会覆盖绝大多数外部/内嵌选择器规则(除非外部/内嵌使用 !important)。
  3. 特异性数值示例(从高到低的"权重"理解):
    • 内联样式(style attribute) → 记作最高(常表述为 1,0,0,0)
    • ID 选择器 #id → 比如 (0,1,0,0)
    • 类/属性/伪类 .class, [attr], :hover → (0,0,1,0)
    • 元素/伪元素 div, p, ::after → (0,0,0,1)
  4. 当特异性相同,后出现的规则覆盖先出现的(CSS 文件加载顺序和位置决定「后来者获胜」)。
html 复制代码
<div id="box" class="green" style="color: red;">文本</div>
  • .green { color: green; } 在外部样式中定义,最终文本是 红色 (因为内联 style 覆盖)。
  • 如果外部写了 .green { color: green !important; },则 绿色 会胜出(author !important 与 author 普通规则比较时 !important 胜出)。
  • 如果内联也写了 style="color:red !important",那内联的 !important 胜。

三种样式的比较和推介使用

很显然,有三种样式存在的方式,我们就可以在不同的场景下进行灵活选择。

行内样式(inline)

行内样式(inline)的好处就是立刻生效、简洁,适合单次小调整或由 JS 动态设置样式element.style)。对于临时调试、或邮件模板中(很多邮件客户端不支持外部样式)非常有用。但是现代大型工程显然不推介这种方式,因为它破坏结构与样式分离(HTML 混入样式)规则,难维护、重复代码多;特异性很高,容易引发覆盖困难("特异性战争")。除了这种维护困难,性能上也会出现大问题:无法被浏览器单独缓存(每个页面都会重复);最致命的也许是与 CSP(Content Security Policy)冲突:许多站点禁止 style-src 'unsafe-inline',所以行内可能被阻止。所以笔者注意到,除非有很正当的理由,否则不要用。

内联/嵌入式样式(<style>

内联/嵌入式样式便于将某个页面的样式集中在同一文件中,适合单页或独立页面的局部样式。但是他也有麻烦,比如说不同页面之间不能被缓存重用(如果多个页面都内嵌同样样式,会重复传输),而且,好像还是没有分离的很开,仍然会给我们的开发造成大量的麻烦,所以这种方式只有在单页应用或页面级样式、调试、快速原型、或构建时将 critical CSS 放到 <head> 中以优化首屏渲染的时候使用

外部样式表(.css 文件 + <link>)⭐⭐⭐⭐⭐

这才是大家使用的最多的方式,外部样式表可以让我们的渲染样式集中、可重用、便于维护、可以被浏览器缓存,减少重复传输;此外易于与构建工具和版本控制配合


CSP(Content Security Policy)与内联样式

很多站点出于安全会设置 CSP,例如:

http 复制代码
Content-Security-Policy: style-src 'self';

这会拒绝任何行内样式或未授权的内联 <style>

选择器:选择器的概念与分类(为什么选择器重要)

很经典的问题:你怎么让浏览器知道你要渲染谁,对哪一个元素进行渲染?选择器就是告诉浏览器------你要渲染哪一批元素。他就是CSS 的"定位"机制:告诉浏览器把哪些样式应用到文档中哪些元素上。

  • 简单选择器(类型/类/ID/通配符)
  • 属性选择器(基于元素属性)
  • 组合器(描述元素之间的关系)
  • 伪类 / 伪元素(状态与虚拟元素)
  • 选择器分组(用逗号合并多个选择器)

选择器写得好,样式更可读、可维护、且能避免不必要的渲染开销。


基本(简单)选择器详解

元素选择器(type selector)

这个选择器是直接Target到了指定的元素了,比如说p标签,比如说div标签等等。

复制代码
p { line-height: 1.6; }
nav { display: flex; }

所以这种标签主要用的是针对全局的设置。

类选择器(class selector)

最常用、推荐的选择方式。以 . 开头,匹配 class 属性包含该类名的元素。

复制代码
.btn { padding: 8px 12px; }
.card.header { /* 同时包含 .card 和 .header */ }

低特异性、复用性高、和组件化配合良好(BEM 等)。

ID 选择器(id selector)

# 开头,匹配 id 属性相同的单个元素。

复制代码
#main { max-width: 1200px; margin: 0 auto; }

注意:ID 的特异性高,且通常页面中只应出现一次。现代团队常建议用类替代 ID 做样式(ID 多用于 JS 钩子或锚点)。

通配选择器(universal selector)

*,匹配所有元素(注意性能与广度)。

复制代码
* { box-sizing: border-box; }

注意的是,这种选择器往往在开发中被禁用掉(或者是拿来做清除某一些效果用的)


属性选择器(attribute selectors)

属性选择器通过元素属性筛选元素,形式灵活,常用于无类库或第三方组件的定制。

  • [attr]:存在某属性
    input[required] { border-left: 3px solid #f00; }
  • [attr="value"]:完全匹配
    a[target="_blank"] { /* 新窗口链接 */ }
  • [attr~="value"]:空格分隔的词列表中匹配一个词(常用于 class 备份场景)
    *[class~="btn"] { /* 等同于 .btn */ }
  • [attr|="value"]:以 value 或 value- 开头(常用于语言)
    html[lang|="en"] { }
  • [attr^="value"]:以 value 开头(prefix)
    a[href^="mailto:"] { /* 邮件链接 */ }
  • [attr$="value"]:以 value 结尾(suffix)
    img[src$=".svg"] { }
  • [attr*="value"]:包含 value(substring)
    a[href*="example.com"] { }

属性选择器对动态属性和第三方生成的 DOM 很有用,但表达式复杂时要注意性能与可读性。


组合器(combinators)与关系选择器

描述元素之间的父子/兄弟/后代关系。

  • 后代选择器(space)A B:匹配位于 A 后代(任意层级)中的 B

    复制代码
    article p { margin-bottom: 1rem; }
  • 子选择器(>A > B:匹配 A 的直接 元素 B(这里谈到的子是直接的儿子,不包含孙子曾孙子等)

    复制代码
    ul > li { list-style: none; }
  • 相邻兄弟选择器(+A + B:匹配直接跟在 A 之后的兄弟 B

    复制代码
    h2 + p { margin-top: 0.25rem; }
  • 通用兄弟选择器(~A ~ B:匹配在同一父元素下、位于 A 之后的所有兄弟 B

    复制代码
    input:checked ~ .details { display: block; }

选择器越具体、越嵌套越深,维护成本越高。优先使用类并限制层级深度(例如不超过 3 层复杂链)。


伪类(pseudo-classes)与伪元素(pseudo-elements)

常见伪类(表示元素状态)
  • :hover:active:focus:visited:checked:disabled:nth-child():nth-of-type():not() 等。

    button:hover { transform: translateY(-2px); }
    li:nth-child(odd) { background: #f8f8f8; }
    a:visited { color: purple; }

:not() 是选择器否定伪类;注意它本身不增加特异性,但它参数的特异性会被计入

常见伪元素(创建虚拟子元素)
  • ::before::after::first-line::first-letter 等(现代语法使用双冒号 ::,单冒号兼容旧浏览器)。

    .btn::before { content: "★ "; }
    p::first-letter { font-size: 2em; float: left; }

伪类用于"状态"、伪元素用于"生成内容或样式化文档的一部分"。两类常搭配使用(例如 a:hover::after { ... })。


选择器优先级(specificity)与覆盖规则

基本原则

按优先级顺序比较:

  1. Inline 样式(style="")最高(记作 1,0,0,0
  2. ID 选择器(#id)计入到第二位(比如 0,1,0,0
  3. 类/属性/伪类(.class, [attr], :hover)计入第三位(0,0,1,0
  4. 元素/伪元素(div, ::after)计入第四位(0,0,0,1
  5. !important 可以覆盖常规顺序(优先比较是否 !important,再比较特异性)

表示法:把特异性写成 (a,b,c,d) 四元组,逐位比较,先看 a,再看 b,以此类推。

Example: 数字逐位计算(按要求逐位算清楚)

选择器:#nav .item > a:hover::after

我们把它拆解计数:

  • #nav → ID:计为 b = 1
  • .item → 类:计为 c = 1
  • a → 元素:计为 d = 1
  • :hover → 伪类:也是类级别,计为 c += 1
  • ::after → 伪元素:元素级,计为 d += 1

逐位汇总(按顺序 a,b,c,d):

  • a(inline) = 0
  • b(ID) = 1
  • c(类/伪类/属性) = 1(.item) + 1 (:hover) = 2
  • d(元素/伪元素) = 1 (a) + 1 (::after) = 2

最终特异性 = (0, 1, 2, 2)。任何具有更高 b(ID)或在 b 相同下更高 c 的规则会覆盖本规则;若所有位相同,则文档中后出现的规则胜出(later wins)。


7 --- 选择器分组、层叠规则与顺序

  • 分组选择器(用逗号):复用样式,等价于多条独立规则,但写起来更短。

    h1, h2, h3 { font-family: "Noto Sans", sans-serif; }

  • 层叠(Cascading)规则 :当多个规则匹配同一元素,按照先比较 !important,再比较来源(user/author/UA),再比较特异性,最后比较出现顺序(later wins)。

提示:常见错误是把样式写在多个文件,然后忘了加载顺序,导致覆盖混乱。保持样式文件结构清晰,约定好全局与组件样式的加载顺序。

相关推荐
im_AMBER1 小时前
Leetcode 46
c语言·c++·笔记·学习·算法·leetcode
浪裡遊2 小时前
Next.js路由系统
开发语言·前端·javascript·react.js·node.js·js
卡提西亚2 小时前
C++笔记-20-对象特性
开发语言·c++·笔记
mapbar_front2 小时前
职场中的顶级能力—服务意识
前端
2301_796512522 小时前
Rust编程学习 - 内存分配机制,如何动态大小类型和 `Sized` trait
学习·算法·rust
尽兴-2 小时前
[特殊字符] 微前端部署实战:Nginx 配置 HTTPS 与 CORS 跨域解决方案(示例版)
前端·nginx·https·跨域·cors·chrom
秦明月133 小时前
EPLAN电气设计:快捷键版本差异解析
经验分享·学习·学习方法·设计规范
JIngJaneIL3 小时前
助农惠农服务平台|助农服务系统|基于SprinBoot+vue的助农服务系统(源码+数据库+文档)
java·前端·数据库·vue.js·论文·毕设·助农惠农服务平台
云外天ノ☼3 小时前
待办事项全栈实现:Vue3 + Node.js (Koa) + MySQL深度整合,构建生产级任务管理系统的技术实践
前端·数据库·vue.js·mysql·vue3·koa·jwt认证