12k
All articles

别让标题在错误的位置换行

用合适的 CSS 和 HTML 修复标题断行和长 URL:nbsp、wbr、overflow-wrap:anywhere、word-break:break-all、hyphens:auto、text-wrap。

OpenReplay Team
OpenReplay Team
别让标题在错误的位置换行

标题在一个短词后换行、最后一行只孤零零地留下一个单词、一个长 URL 撑宽了它的容器——这是三种不同的缺陷,每一种通常都需要不同的 CSS 或 HTML 修复方案。

它们还有一个共同的恼人特性:只在特定的容器宽度下才会出现。设计师在 384px 的手机上截了张标题错乱的图,你在桌面宽度下打开页面,一切看起来都很正常。糟糕的换行往往藏在没人测试过的窄视口区间里,这也是为什么在真实用户实际浏览的宽度下观看 session replay,往往是重现该缺陷的最快方式。

本文将逐一梳理每种可见问题、对应的修复工具,以及帮助你下次选对方案的判断思路。

核心要点

  • 处理长 URL 和 token 时,请使用 overflow-wrap: anywhere 而非 break-wordanywhere 会把它的紧急断点计入 min-content 尺寸计算,因此 flex 或 grid 项目才能真正收缩。
  • 要禁止在某一处换行,请把可断字符替换为它的不换行版本:空格用  (U+00A0),连字符用 ‑(U+2011),完全没有空格的位置用 ⁠(U+2060)。
  • 要允许在某一处换行,可使用 <wbr> 实现无痕断行,或使用 &shy;(U+00AD)——它在未断行时不可见,一旦断行则渲染出一个连字符。
  • word-break: break-all 会在每一行末尾切断普通单词;请将它保留给哈希值和 token 这类单词边界毫无意义的字符串。
  • text-wrap: balance 只会重新分配已有的断点,绝不会改变元素尺寸,而且浏览器对它有行数上限:Chromium 为六行,Firefox 为十行。

如何禁止在某一处换行?

要阻止浏览器在某个特定位置断行,就把可断字符替换成它的不换行版本。不换行空格(&nbsp;,U+00A0)会把两个词之间的普通空格替换成浏览器不允许断开的空格,从而让它们保持在同一行。对于在 “of” 之后立即换行的标题,或产品名被拆成两行的情况,这就是解决办法。

文本也可能在没有空格的位置断行,最常见的是在连字符之后。word joiner(U+2060,写作大小写敏感的 &NoBreak; 命名引用)在不增加任何宽度的情况下禁止断行;而不换行连字符(U+2011,&#8209;,没有对应的命名实体)会渲染出一个不能成为断点的连字符:

<h2>Shipping the new editor to&nbsp;everyone</h2>
<p>Take the exit for I&#8209;95 north.</p>

如何允许在某一处换行?

与之相反的问题是:一个长字符串你希望它能断行,但只能在你指定的位置断。插入一个 <wbr> 元素会为浏览器提供一个额外的行尾候选位置,且断行时不会插入任何可见内容。软连字符 &shy;(U+00AD)标记的是同一类断行机会,但断行时会渲染出一个连字符;在此之前它是不可见的。它在默认的 hyphens: manual 下即可生效,因此只有当某处设置了 hyphens: none 时才会失效。

<p>https://docs.example.com<wbr>/reference<wbr>/text-wrapping</p>
<p>The rise of hyper&shy;modularization</p>

展示型 URL 和标识符用 <wbr>,真正的单词用 &shy;

无法断行的长字符串:overflow-wrap: anywhere

对于用户生成内容、长 URL,以及无法手工标注的 token,请设置 overflow-wrap: anywhere,让浏览器把断行作为最后手段。关键细节在于固有尺寸(intrinsic sizing)。两个值都允许浏览器切分长单词,但只有 anywhere 会让这些紧急断点计入元素的 min-content 宽度;break-word 则会把该宽度保持为整个未断字符串的长度。这就是为什么在 break-word 下,包含长 URL 的 flex 项目仍然拒绝收缩并撑破布局,而在 anywhere 下则能正确收缩。CSS Text Level 4 对这两个值的定义除了这一处差异之外完全相同,而这恰恰是人们实际会踩到的差异。

.card { display: flex; gap: 1rem; }
.card-body {
  /* break-word leaves min-content at the full URL width */
  overflow-wrap: anywhere;
}

anywhere 值在所有现代引擎中都可用,Safari 是最后一个支持的,于 15.4(2022 年 3 月)落地。一些老旧代码库中仍带有已废弃的 word-break: break-word,它的换行效果与 overflow-wrap: anywhere 完全一致,同时让 word-break 本身保持默认行为。请把它替换掉。

word-break: break-all 该用在哪里?

word-break: break-all 会在行内空间用尽的任何位置断行,哪怕是在单词中间。它不会添加连字符,也不会为了保持单词完整而把整个单词移到下一行,因此普通正文会在每一行末尾被切断。这使它成为处理正文的错误工具,却是处理那些单词边界毫无意义的字符串的正确工具:commit 哈希、API key、表格单元格中的 token。它的 keep-all 值则服务于相反的 CJK 需求,抑制字符之间的断行。

.commit-hash {
  font-family: monospace;
  word-break: break-all; /* never on prose */
}

窄栏排版:hyphens: auto 加上 lang 属性

当窄栏迫使单词中间断行时,一个符合词典规则的连字点比粗暴切断要好读得多。除非元素本身或其某个祖先带有 lang 属性,否则 hyphens: auto 不会产生任何效果。每种语言都有自己的断词方式,因此浏览器需要先知道该查哪一种,才能进行连字处理。该属性已达到 Baseline Widely available,自 2023 年 9 月起在各浏览器中无需前缀即可使用。

<aside lang="en" class="sidebar-note">Uncharacteristically long words wrap cleanly here.</aside>
.sidebar-note { hyphens: auto; }

标题与段落:text-wrap 的 balance 与 pretty

对于标题最后一行孤零零的一个单词,text-wrap: balance 会在该行已有的断点中做出不同选择,从而让各行长度趋于均衡;它不会创建新的断点,也不会改变元素盒子的尺寸,因此平衡后的标题仍保持原有宽度。平衡计算开销较大,所以浏览器设置了上限:Chromium 到六行为止,Firefox 到十行,更长的块则按常规换行。text-wrap: pretty 针对的是正文,它会投入更多布局计算来避免孤字;Chromium 和 Safari(自版本 26 起)已支持,Firefox 尚未支持。text-wrap 属性本身自 2024 年 3 月起已在各最新浏览器中可用。

h1, h2, h3 { text-wrap: balance; }
p { text-wrap: pretty; }

问题—工具速查表

可见缺陷工具
标题在一个短词后断行在必须保持在一起的词之间加 &nbsp;
在本应保持完整的连字符处断行&#8209;(不换行连字符)或 &NoBreak;
长字符串需要在指定位置断行<wbr>(无连字符)或 &shy;(断行时显示连字符)
URL 或 token 撑宽卡片或网格单元overflow-wrap: anywhere
哈希值溢出表格单元格word-break: break-all
窄栏在音节中间切断单词hyphens: auto 加上 lang 属性
标题最后一行只剩一个单词text-wrap: balance
正文段落出现孤字text-wrap: pretty

整套工具背后的思路是一致的:先判断该缺陷需要的是禁止断行、允许断行,还是整体的换行策略,然后选用能解决问题的最小工具。一个合理的基线配置是:在承载用户内容的容器上使用 overflow-wrap: anywhere,标题上使用 balance,段落上使用 pretty;至于字符级实体,则留给设计师之后逐个标出的具体断行问题。

常见问题

overflow-wrap 和 word-break 有什么区别?

overflow-wrap 只在某个单词长到无法单独占据一行时才介入,其余内容仍按常规换行。word-break: break-all 则改变整个元素的换行策略,会在行内空间用尽的任何位置切断单词。对于正常正文中偶尔出现的长 URL,请使用 overflow-wrap: anywhere;只有在单词边界毫无意义的哈希和 token 上才使用 word-break: break-all。

word-wrap 和 overflow-wrap 是同一个东西吗?

是的。该属性最初是微软的扩展,名为 word-wrap,后来采用了标准名称 overflow-wrap,浏览器至今仍把旧名称当作新名称的别名接受。新代码中请写 overflow-wrap;当前浏览器不需要 word-wrap 作为回退,同时声明两者的样式表只是把同一个属性设置了两遍。

什么时候该用零宽空格而不是 wbr 元素?

当你只能修改字符串本身时(例如存储在 CMS 中或由 API 返回的内容),使用零宽空格(U+200B,写作 ​ 引用);当你能控制 HTML 时,使用 wbr 元素。零宽空格是一个留在文本中的不可见字符,因此会随复制粘贴一起传播,并可能干扰精确匹配搜索。

在不支持 text-wrap: pretty 的浏览器中会发生什么?

该声明会被直接忽略,文本按默认的逐行算法换行,视觉上不会出现任何破损。CSS 会丢弃它无法识别其值的声明,这使得 text-wrap: pretty 和 text-wrap: balance 成为安全的渐进增强:支持的浏览器获得更好的断行效果,其余则与之前渲染完全一致。无需特性查询或回退声明。

Open-source session replay

Gain control over your UX

See how users are using your site as if you were sitting next to them, learn and iterate faster with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.

Star on GitHub12k

We use cookies to improve your experience. By using our site, you accept cookies.