属性选择器匹配的是dom中真实存在的html属性,而非js对象或框架响应式数据;动态属性未被选中,通常因该属性根本未渲染到dom中,需用开发者工具确认其是否存在、拼写是否正确、引号是否缺失、大小写是否一致,且属性选择器必须紧接元素名后、不可插入伪类中间。

属性选择器本身不会“失效”,它匹配的是当前 DOM 节点上真实存在的 attribute(HTML 属性),而不是 JS 对象属性、Vue/React 的响应式数据,或尚未渲染到 DOM 的值。动态属性没被选中,99% 是因为那个 data-xxx 属性压根没出现在最终的 HTML 节点上。
动态属性根本没写进 DOM,自然选不到
最常见的原因是:JS 或框架在模板里写了 v-bind:data-id="id" 或 :data-status="status",但 id 或 status 变量是 null、undefined 或空字符串,结果 Vue/React 直接跳过该 attribute 的渲染——DOM 里查无此属性,[data-id] 当然不匹配。
- 用浏览器开发者工具 Elements 面板右键目标元素 →「Edit as HTML」,确认该 attribute 是否真实存在(不是“看起来有”,而是源码里真写了)
- 检查是否拼错名字,比如
data-user_id(下划线)写成data-user-id(短横线),CSS 里却用[data-user-id] - SSR 场景下,服务端渲染时变量未定义,导致首屏缺失 attribute;客户端 hydration 后才补上,但样式计算早已完成
属性值含空格或特殊字符,但 CSS 里没加引号
[data-status=active] 看似简洁,但一旦实际 HTML 是 <div data-status="in progress">,不加引号就会解析失败。CSS 规范要求:值含空格、<code>/、:、# 或以数字开头时,必须用引号包裹。
- 推荐统一用单引号:
[data-role='header']、[data-id='123abc']、[src$='.svg'] -
[type='text/email']能匹配type="text/email",但现实中极少遇到;更常见的是[type='email']匹配type="email" - 别写
[data-status:"active"](冒号)或.data-status="active"(点号+等号),浏览器直接忽略整条规则
大小写不一致,又没加 i 标志
HTML 中 type="TEXT" 和 type="text" 是等价的,但 CSS 属性选择器默认区分大小写:[type='text'] 不会匹配 type="TEXT"。
- 优先保持 HTML 和 CSS 书写一致(全小写),这是最省心的做法
- 确实需要忽略大小写时,用
[type='text' i]—— 注意i是 CSS Level 4 特性,Chrome 63+/Firefox 70+/Safari 15.4+ 支持,IE 全系不支持 -
data-自定义属性也适用此规则:[data-role='Header' i]可匹配data-role="header"或data-role="HEADER"
伪类和属性选择器顺序写反了
CSS 解析器对语法顺序很敏感。input:focus[type='text'] 是无效的,会被整个丢弃;而 input[type='text']:focus 才是合法写法。
- 属性选择器必须紧跟在元素名、类名或 ID 之后,不能插在伪类中间
- 组合使用时,确保结构清晰:
button[disabled]:hover✅,button:hover[disabled]❌ -
:has()内部可以放属性选择器(如:has([data-loaded])),但它本身不能作为属性选择器的一部分
真正容易被忽略的点是:属性选择器只认“已落地”的 attribute,不关心 JS 有没有赋值、框架有没有响应、控制台打印出来是不是“有”。打开 Elements 面板,盯着那个节点看——它身上到底有没有那串字,才是唯一真相。











