:not(:empty) 匹配至少含一个非空白文本节点、子元素或注释的元素,而非“有可见内容”的元素;它无法过滤仅含空白符的元素,需JS配合innerText.trim()等判断真实内容。
:not(:empty) 的实际行为:not(:empty) 并不是“排除有内容的元素”,而是匹配「不满足 :empty 条件」的元素——即至少含一个非空白文本节点、子元素或注释的元素。它常被误认为是“选中有文字的元素”,但其实只要存在一个空格、换行符或 <span></span>,该元素就不算 :empty,就会被 :not(:empty) 匹配到。
常见错误现象:div:not(:empty) { color: red; } 对看似“空”的 div 仍生效,只因 HTML 中写了 <div>n </div>(含换行+缩进空格)。
:empty 严格要求:无子节点(包括文本节点),且文本节点不能仅含空白符(U+0020、U+0009、U+000A、U+000C、U+000D)<div></div> 是空的,<div>n</div> 不是:not(:empty) 等价于 :is(:not(:empty)),但无法进一步“过滤掉仅含空白的元素”——CSS 没有 :blank 或 :has-text
CSS 本身无法判断“是否渲染出可见内容”,比如 <div style="display:none">hi</div> 或 <div><span style="visibility:hidden">x</span></div> 都不算 :empty,但用户看不见。此时必须用 JavaScript 做语义化判断。
实操建议:
立即学习“前端免费学习笔记(深入)”;
element.innerText.trim() !== '' 判断是否含有效文本(自动忽略隐藏元素、样式影响)getComputedStyle(element).display !== 'none' && element.offsetWidth > 0 排除不可见容器DOMSubtreeModified(已废弃),改用 MutationObserver 观察 childList 和 characterData
.item 加 class:const observer = new MutationObserver(() => { document.querySelectorAll('.item').forEach(el => { el.classList.toggle('has-content', el.innerText.trim() !== ''); });});
:not() 嵌套与伪类组合的兼容性陷阱:not() 在 CSS 中只接受简单选择器(simple selector),不能写 :not(:empty, :hidden) 或 :not(div:empty)。Chrome/Firefox 支持 :not(:empty):not([data-pending]) 这种链式写法,但 Safari 15.4 及更早版本对多伪类 :not() 组合支持不稳定。
div:not(:empty, [data-status="pending"]) → 语法错误,浏览器直接丢弃整条规则div:not(:empty):not([data-status="pending"])
!important 覆盖(不推荐),或用 JS 动态加 class 控制:not(:empty):has(*) 类组合也容易失效,:has() 本身仍是实验性特性:empty 做“占位提示”比 :not(:empty) 更可靠与其费力用 :not(:empty) 筛选非空元素,不如反向思维:用 :empty 直接控制空状态样式,再让非空元素继承默认样式。这样逻辑清晰、兼容性高、无需担心空白符干扰。
td { color: #333; }td:empty::before { content: "/"; color: #999; font-style: italic; }
el.textContent = el.textContent.replace(/^s+|s+$/g, ''),再靠 :empty 判定data-empty="true" 属性,规避客户端解析空白符的不确定性:not(:empty) 表面灵活,实则边界模糊;真正可靠的动态筛选,往往得在 JS 层做一次语义清洗——别指望纯 CSS 能读懂“人眼看到的空”。