最可靠方式是用 tbody tr:hover,因为 :not(thead) 无法筛选祖先元素,其语法含义是“非 thead 元素内的 tr”,而 tr 的父级是 table,故始终匹配所有 tr;且浏览器自动补全 tbody,必须显式写出才能精准命中数据行。

直接写 tr:hover 会连表头一起高亮,必须排除 thead ——最可靠方式是用 tbody tr:hover,:not(thead) 在这里不适用、不可靠。
为什么 :not(thead) tr:hover 不起作用?
CSS 中 :not(thead) 是伪类,只能作用于元素自身,不能用来“筛选父容器”。thead tr:hover 和 tbody tr:hover 是两个独立的选择器路径;而 :not(thead) tr 语法上等价于 “一个不是 thead 的元素内部的 tr”,但 tr 不可能在 thead 外面直接嵌套——它总在 table 下,而 table 不是 thead,所以这个选择器实际会匹配所有 tr,包括 thead 里的,毫无排除效果。
常见错误现象:tr:not(thead) tr:hover 语法非法;:not(thead) > tr:hover 永远不匹配,因为 tr 的父级是 table 或隐式 tbody,不是 thead。
真正有效的写法:只对 tbody 内的 tr 绑定 hover
现代浏览器中,即使 HTML 源码没写 tbody,解析后 DOM 也会自动补全。因此 CSS 必须显式写出 tbody 才能精准命中数据行。
-
tbody tr:hover { background-color: #f0f9ff; }—— 简洁、语义清晰、100% 排除thead和tfoot - 若表格含
tfoot且不想让它响应 hover,无需额外处理,tfoot tr不在tbody下,自然不匹配 - 加过渡动画时,统一给
tbody tr设transition: background-color 0.2s;,避免只在 hover 规则里写导致部分浏览器不生效
IE8 及更老浏览器下 tbody tr:hover 完全无效
这些版本不支持 :hover 作用于非链接元素(a 除外),tr 被视为普通元素,伪类被忽略。
- 无法靠 CSS 降级解决,必须用 JS 补充:监听
mouseenter/mouseleave,动态增删 class,例如is-hovered - 如果项目已弃用 IE8,可忽略;若仍需兼容,建议统一用 JS 方案,而非混用 CSS + 条件注释
- 注意:jQuery 的
.hover()在 IE8 下有事件冒泡 bug,推荐用原生addEventListener或封装好的轻量事件委托
移动端点击后 hover 闪一下就消失,不是 bug 是规范
iOS Safari 和 Android Chrome 都遵循 Pointer Events 规范:触摸设备上,:hover 仅在“模拟鼠标悬停”阶段短暂触发(比如长按后抬起前),且不会持续维持。
- 不要指望
tbody tr:hover在手机上提供稳定视觉反馈 - 如需一致体验,改用
@media (hover: hover)做条件启用:@media (hover: hover) { tbody tr:hover { background-color: #f0f9ff; } } - 或改用
tr:active+ JS 添加临时 class,配合touchstart/touchend控制生命周期
真正麻烦的从来不是写一行 tbody tr:hover,而是忘记浏览器自动补 tbody 却在 CSS 里漏写它,或者误信 :not() 能过滤祖先结构——这两点,90% 的“hover 不生效”问题都出在这儿。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











