::first-letter对下划线文本无效,因下划线“_”不属于Unicode字母类,不满足CSS规范中“可排版为字母的字符”要求;其生效需同时满足父元素display为block等、首字符为Unicode字母、无前置空白及未被定位等破坏流样式干扰。

为什么 ::first-letter 对带下划线的文本无效
根本原因在于:浏览器只对纯文本节点的首字符应用 ::first-letter,一旦首字符是 _(下划线),它会被视为标点或分隔符,在多数字体和渲染引擎中被排除在“字母”范畴之外。CSS 规范明确要求该伪元素仅作用于“可排版为字母的字符”,而 _ 不属于 Unicode 字母类(Lu/Ll/Lt/Lm/Lo/NL),因此直接失效。
::first-letter 的实际生效条件
必须同时满足以下全部条件,伪元素才会触发:
- 父元素
display值为block、inline-block、table-cell或list-item - 首字符是 Unicode 字母(如
A–Z、a–z、中文、日文平假名等),不是_、-、'、"等标点 - 首字符前无空格、换行、
或其他不可见字符 - 未被
float、position: absolute等破坏正常流的样式干扰
用文本节点选择 + 字体属性绕过限制
既然伪元素走不通,就手动定位首字符——把下划线后的第一个字母包裹进 <span>,再单独设置样式。关键点是:确保该字母在 DOM 中是独立文本节点的开头,且不被父级 font-family 影响字形表现。
<p class="title">_Hello world</p>
<p><style>
.title {
font-family: "Segoe UI", system-ui, sans-serif;
}
.title span:first-of-type {
font-size: 2em;
font-weight: bold;
color: #2563eb;
/<em> 避免因字体缺失导致字形塌缩 </em>/
font-feature-settings: "liga" on, "clig" on;
}
</style></p><p><script>
document.querySelectorAll('.title').forEach(el => {
const text = el.textContent;
const match = text.match(/^<em>([a-zA-Z\u4e00-\u9fa5])/);
if (match) {
const firstLetter = match[1];
const rest = text.slice(match[0].length);
el.innerHTML = `</em><span>${firstLetter}</span>${rest}`;
}
});
</script>容易被忽略的兼容性细节
手动包裹方案看似简单,但有三个隐蔽坑:
立即学习“前端免费学习笔记(深入)”;
-
innerHTML会销毁原有子元素(比如内嵌链接或图标),需先提取并重建结构 - 中文场景下,
\u4e00-\u9fa5范围不覆盖所有汉字(缺繁体、生僻字、扩展区),建议改用/^_\p{Letter}/u正则(需运行环境支持 Unicode 属性转义) - 若父容器设置了
text-transform: uppercase,要同步在<span>上设置,否则大小写不一致
真正难的不是加样式,而是判断「谁才算第一个有效字母」——尤其当文本来自 CMS 或用户输入时,前置空格、零宽字符、BOM 都会让 textContent[0] 失效。动手前先 console.log(JSON.stringify(text[0])) 看一眼真实字符码。










