应使用 Array.from(str) 转字符数组再截取,避免 slice() 按 UTF-16 码元截断导致中文、emoji 显示异常;需判断原长是否超限,再决定是否加省略号。

直接用 slice() 截取会切掉半个中文或标点
很多人写 str.slice(0, 60) + "...",结果标题末尾出现“…”,或者“李”字被砍成乱码——因为 slice() 按 UTF-16 码元计数,一个中文字符占 2 个码元,但视觉上是 1 个字符。尤其在含 emoji、生僻汉字或代理对(surrogate pair)的标题里,slice(0, 60) 可能截在中间,渲染异常。
真正安全的做法是:先用 Array.from(str) 转成字符数组(正确处理 Unicode),再截取,最后拼接省略号。
-
Array.from(str)能识别 emoji、中文、拉丁字母等所有 Unicode 字符,长度即“人眼看到的字数” - 若原始字符串已知为纯 ASCII(如英文新闻源),可跳过这步,直接用
slice() - 注意:IE 不支持
Array.from(),需加 polyfill 或改用String.prototype.split('')(但后者对 emoji 仍不完全可靠)
slice() 的参数边界容易错:60 是含省略号还是不含?
常见误写:str.slice(0, 60) + "..." → 实际显示 63 字符,超出设计宽度;更糟的是,有人写 str.slice(0, 57) + "..." 却忘了判断原字符串是否本就 ≤57,导致多加省略号。
正确逻辑必须带长度判断:
function truncateTitle(str, maxLength = 60) {
if (typeof str !== 'string') return '';
const chars = Array.from(str);
if (chars.length <= maxLength) return str;
return chars.slice(0, maxLength).join('') + '...';
}
-
maxLength指“文字部分最大长度”,不包含...——这是 UI 设计侧的真实约束 - 返回前务必检查
typeof str,避免null或undefined导致Array.from()报错 - 不要用
substr(),它已被废弃,且负索引行为与slice()不一致
服务端预截断比前端 JS 更稳,但得同步规则
如果新闻列表页由后端模板(如 PHP、Java)渲染,建议在服务端统一截断。否则前后端对“60 字符”的理解可能不一致:PHP 的 mb_substr($str, 0, 60, 'utf-8') 和 JS 的 Array.from(str).slice(0, 60) 大概率结果一致,但若后端用 substr() 或没指定编码,就会出偏差。
- 前后端共用同一套截断函数(如封装为公共工具库)最保险
- 若无法共用,至少约定:都以 Unicode 字符数为准,而非字节或码元
- 特别注意:数据库字段长度限制(如
VARCHAR(255))按字节算,和前端显示长度不是一回事
超长标题点击展开时,别只靠 JS 截断恢复原内容
有些实现把完整标题存在 data-full-title 属性里,点击“展开”再塞回去。这看似简单,但隐患不少:
- 若原始标题含 HTML 标签(如
<strong>突发</strong>),直接 innerHTML 渲染有 XSS 风险 - 若标题含换行符
\n,前端未做innerText转义,可能破坏布局 - 更稳妥做法:服务端返回结构化数据,如
{ truncated: "美联储宣布...", full: "美联储宣布新一轮量化宽松政策..." },前端只负责展示
真正难的从来不是怎么切,而是切完之后,用户看到的“…”到底代表什么、能不能准确还原、有没有意外副作用——这些细节,往往在第一次上线后才暴露。

















