空单元格显示“—”应通过 CSS ::before 插入长破折号实现,而非依赖 font-variant-ligatures;:not(:has(\*)) 可应对伪空单元格,IE11 需 JS 判空,后端填充才是根本解法。

空单元格显示连字符(—)不是靠字体连字特性,而是内容占位问题;font-variant-ligatures 对空单元格完全无效,别在这儿浪费调试时间。
空单元格根本不会触发 ligatures
连字(ligature)是字体对**相邻字符组合**的渲染优化,比如 fi、fl; 、- 或空字符串都不是“相邻字符”,更不构成 OpenType 的 liga 特性触发条件。浏览器连尝试渲染连字的机会都没有。
-
td:empty::before { content: "-"; }插入的是普通短横线,不是连字,和font-variant-ligatures无关 - 即使你给
td设置了font-variant-ligatures: common-ligatures,里面只有-也绝不会变成任何连字 - 所谓“连字符占位”是误称——你要的只是视觉上统一的空白符号,不是字体特性
用 ::before 插入短横线最稳妥
这是目前兼容性最好、可控性最强的方式,尤其适合动态表格(Vue/React 渲染后 DOM 可能含空格或注释,导致 :empty 失效):
- 写
td:not(:has(*))::before { content: "—"; color: #999; font-size: 0.9em; }——用长破折号—(U+2014),比短横-更符合数据表语义 - 不用
:empty,改用:not(:has(*)):它能排除含空格、换行、注释甚至隐藏span的“伪空”单元格 - 避免直接写
content: "-":短横易被误读为减号或布尔值 false,在数值列中干扰右对齐 - 若需支持 IE11,
:has()不可用,退回到 JS 判空 + 添加data-empty类再用 CSS 控制
后端模板层填充才是根治方案
前端补救总会有漏网之鱼,尤其面对 null、undefined、""、0、false 混杂的字段:
立即学习“前端免费学习笔记(深入)”;
- PHP:
echo $value === null || $value === '' ? '—' : htmlspecialchars($value); - JavaScript 模板:
cellValue == null ? '—' : cellValue(注意== null能捕获null和undefined,但不捕获""或0) - Thymeleaf:
${item.price ?: '—'}(仅对 null/undefined 生效;若字段可能为空字符串,得用${#strings.isEmpty(item.price) ? '—' : item.price}) - 关键:数字列统一用
—,文本列可用-或留空加浅灰背景,保持列内对齐一致性
最容易被绕过的点是嵌套结构——比如 td 里包了个 div,哪怕 div 是空的,:has(*) 仍返回 true;这时候连 :not(:has(*)) 都失效,必须回到数据源头清洗或加 JS 运行时判断。



















