p { } /* 类型选择器:命中所有 p,权重 0,0,1 */
.card { } /* class:权重 0,1,0 —— 日常主力 */
#header { } /* id:权重 1,0,0 —— 别拿来写样式 */
* { } /* 通配:命中所有元素,权重 0,只适合做重置 */
*, *::before, *::after { box-sizing: border-box; } /* 唯一的正当用途 */
我是 .box 的直接子元素 → 同时被 .box p 和 .box > p 命中(黄底)。
我隔了一层 div → 只被 .box p 命中(无底色)。
h4 + h4 命中,紫色一个段落 这个 span 在 p 之后,被 p ~ span 命中(蓝底)
.card p { } /* 空格 = 后代:任意深度 */
.card > p { } /* > = 子代:只下一层 */
h4 + h4 { } /* + = 紧邻兄弟:紧跟在后面的那一个 */
p ~ span { } /* ~ = 后续兄弟:之后的所有同级 */
A B C > D { } /* 可以混用,从右往左读:D 是 C 的子,C 是 B 的后代… */
选择器是从右往左匹配的,所以最右边那一段(关键选择器)越具体,匹配越快。.list-item 好于写成长链 body > div .box ul li a——长链既慢又脆,DOM 一调整就断。
下图给每个链接叠了不同规则:蓝框 = 有 target;浅蓝底 = href 以 https 开头;红底 = 以 .pdf 结尾;粗体 = href 含 docs;绿色虚线 = rel 里有 noopener;加了字距 = class 等于 btn 或以 btn- 开头。
a[href] /* 存在该属性 */ a[href="x"] /* 精确等于(区分大小写) */ a[href="x" i] /* 忽略大小写 */ a[href^="https"] /* 前缀 */ a[href$=".pdf"] /* 后缀 */ a[href*="docs"] /* 包含 */ a[rel~="noopener"]/* 空格分隔的值列表中包含 */ a[class|="btn"] /* 等于 btn 或以 "btn-" 开头(历史用于 lang 值) */
:nth-child(2) 加粗:last-child 去掉下边框:first-child / :last-child 首尾 :nth-child(2n) / :nth-child(even) 偶数;(2n+1)、odd、from-end(2) 都可以 :nth-child(-n+3) 前 3 个 :only-child 独子 :empty 没有子节点也没有文本(注意空白符也算文本) :not(.is-done) 排除 :is(h1, h2, h3) 见第十五章
把鼠标移上来 / 用 Tab 聚焦我 —— :hover、:focus、:focus-visible、:active 描述状态(单冒号);::before、::after、::placeholder、::selection 创建不存在于 DOM 的额外内容(双冒号,第十七章展开)。历史写法单冒号仍兼容,但新代码请统一双冒号。