使用 <wbr> 解决长 URL 的换行问题
问题
我们知道,世界上文字主要有两种:一种是以中文为代表的象形文字;另一种是以英法俄等为代表的拼音语系。前者的换行很简单,每个单字都有自己的意义,所以每个字后面都可以换行。拼音语言,字母组合本身无意义,连在一起才有意义;不同单词意义差异巨大,所以只能以单词为单位换行。
Web 开发中,屏幕宽度有限,超长文字必须换行。在 CSS 中,控制换行的属性主要有 word-break
,white-space
,其中,默认换行行为的是 word-break: normal
,即以单词为单位换行。比较奇怪的是,对于 URL,我本以为类似 /.:?&
都是明显的单词分隔符,理应换行,但实际上,浏览器并不会在这些地方换行。如果我们使用 break-all
或者 break-word
,则会使得浏览器在不合理的地方换行,如果刚好在表格里,别的列内容比较多,那么包含 URL 的单元格就会被挤压得非常窄,拉得特别高,非常难看,非常难读。
尝试
原生方法无法解决问题,只好摸索手动断行的做法。但是想完美解决问题非常困难:
第一个方案是全部换行,肯定不行;
第二个方案固定宽度换行,因为表格内容不固定,效果也很差,也不行;
老板提出了第三个方案:使用“8.3”格式,即超长字符串只保留前8个字符,后面显示“...",然后可以手动展开。很明显,这个方案对 URL 来说没有什么价值,https://
加起来正好 8 个字符,有意义么……即使加长也一样,因为用户有时候看域名,有时候看 pathname,也有时候看 search,我们没有办法预测。
然后老板又提出“Excel 方案”,即固定列宽,自动隐藏超出的文字,用户可以通过拖拽来调整列宽。这个方案理论上可以解决问题,但是实现难度太大,因为浏览器自带表格自适应宽度的算法,采用 “Excel 方案” 就必须放弃这个算法自己手动实现,成本很高,非万不得已也不想做。
最后,动态换行,根据表格宽度计算在哪里断行。还是不行,计算难度太大。
用 <wbr>
解决
这个问题困扰了我很久,直到前两天,我突然发现原来有 <wbr>
软换行的存在。而且它的兼容性非常之好,甚至连 IE8 都支持。
它的含义是“可换可不换”。当元素宽度不够需要换行,就从它这里换;如果宽度够,就不换行。所以,只需要在“可能”换行的地方加上这个元素,就可以达成我的目标。写成代码很简单,大约是这样:
function wrapUrl(url) { if (!url) { return ''; } // 先把协议取出来,我不希望在协议这里换行 const head = url.substring(0, 10); const left = url.substring(10); // 在 `?&/` 前面插入 `<wbr>` // 或者16个连续英文数字也要换行,打断 hash 和 md5 return head + left.replace(/([?&\/]|([a-zA-Z0-9]{16}))/g, str => '<wbr>' + str ); }
实际效果很好,大概是这样(截图时,<wbr>
放在断开位置的后面,我觉得不好看,就调整了下):
与 <br>
对比,后者是固定换行,当表格内容很少,有充足的空间显示 URL 时,也会换行,就不合适了。
总结
需要注意,<table>
的渲染很特殊,浏览器要花很多时间计算每个列的内容、计算它的宽度,所以性能会比较差,这也是不要用 <table>
做布局的原因。本案例中,使用 <wbr>
实际上是想借用浏览器计算表格各列宽度的机制。所以是合适的。表格渲染之后,内容最好就固定住,不要有复杂的变动,比如隐藏/显示(前面说的8.3格式),因为内容的变化会导致浏览器重新计算布局重新渲染,比较消耗机器的性能。
以及,做了十几年前端,稍一放松,竟然有完全不清楚没用过的标签,看来有必要找时间再把 HTML、CSS 再翻一遍了。
本文首发于我的博客,两边同步更新,欢迎同学与我交流。