javascript - 用三个点替换溢出的字符串字符的问题
问题描述
所以我在stackoverflow上看到了一些解决方案如何做到这一点,但这些解决方案是针对静态数量的字符,例如
if (string.length > 20) {
var shortstring= name.substring(0, 20) + " ...";
}
但这仅适用于 20 个字符,如果元素溢出容器,我希望能够做到这一点,然后用三个点替换这些字符。
例如,
asadasddadasadsssssssssssssssssssssssssssssssssssdddddddddddddddddddddddd
解决方案
确定一段文本是否会溢出布局容器的唯一方法是实际呈现该容器中的文本,然后测量其几何形状。(在 中绘制文本时并非如此<canvas>
,但我相信这是因为画布文本不关心级联样式。)
您看到这么多由字符数驱动的解决方案的原因是因为它更简单,计算成本更低(因此更快),并且因为任何文本元素的尺寸显然都是文本量的函数,(除其他外- 并且有问题),因此人们通常将其视为实际渲染尺寸的快速和肮脏的代理。有句话说:“有时一点点的不准确可以节省大量的解释。” 这就是这里发生的事情。
但这是 StackOverflow,所以让我们挥霍一下解释。系好安全带。如果您想正确执行此操作,请按照以下方法操作:
- 使用所需的文本构造一个文本节点,并将其包装在您知道不会影响其在目标容器中的布局的某个元素中。
这需要深入了解网页上的样式上下文;也许在您的页面 <span>
中.Column2
,它的样式会自动适应其内容,但在我的网站上,目标区域内的每个元素都会收到 2px 填充,因为这是我的布局所需要的。这些细节会影响渲染大小。
- 此外,将新文本元素的不透明度设置为 0。
在将元素添加到目标容器之前执行此操作,否则可能会闪烁。
- 将文本元素插入目标容器。
- 使用 测量文本元素和容器
element.getBoundingClientRect
。 - 对这两个矩形做数学运算。
如果文本元素的宽度或高度大于容器,则溢出。
- 如果文本溢出,请剪掉部分字符串并重新测量。
这可以重复很多很多次,并且 DOM 操作是浏览器可以做的最昂贵的事情之一(相对而言)。这意味着一次简单地删除一个字符是不明智的——如果文本比空间允许的长得多,您的循环可能会重复数千次,用户会注意到这一点,特别是如果您将它应用于多个块页面上的文字;如果在页面加载时触发,这将同时发生在许多元素上,也就是 DOM 抖动:RIP 你的网页。
这意味着您需要更高效的搜索算法。我认为最适合这个的是二分搜索。
- 一旦您确切地确定了哪些字符适合,请删除自定义
opacity:0
,以便文本变得可见。现在你完成了。
您可能可以为该算法添加一些巧妙的优化。例如,为了防止将大量文本塞进小容器中,您可以创建一个空的1em
方形元素,并使用它来确定适合的字符数的上限。因此,第一步可能会将 10k 样本减少到仅 175 个,然后使用猜测和测试过程从那里得到最终值。你可以把这个特殊的短路逻辑放在一个守卫中,只有当文本很长时才会执行。
或者,您可以在浏览器的 localStorage 中缓存最终的字符数,这样刷新具有相同内容的页面就可以读取已经确定的字符数并一步渲染正确数量的文本。我怀疑主要的挑战是为缓存数据设计一个组织方案,因为缓存条目的“坐标”需要包括完整的起始文本和目标容器,如果文本你想使缓存无效或页面样式更改。
许多年前,我使用了一个名为three-dots.js的库来处理这个问题。我一直认为它使用二进制搜索,但现在浏览源代码,似乎并非如此。不过,它说明了这是多么复杂。这就是为什么大多数人都考虑字符数的原因:一个合适的解决方案出奇地复杂而且性能低得多。
你也许可以在 NPM 上找到一个不错的库,或者使用三点.js。你可以自己写,但除非你有无限的时间来做这件事,否则你最好问问自己,在每种情况下都完全正确地做到这一点是否真的那么重要。根据字符数和使用 DevTools 进行的手动测量,保守地截取文本要容易得多,而且可能还不错。
推荐阅读
- angular - SocketIO web socket 必须放在 Angular Service 类上
- php - Laravel Hash::check 返回 false,即使当我用变量的字符串值替换它们时它仍然有效
- python - Selenium 正在返回空列表
- terraform - Terraform - 你有沙箱吗?
- mysql - 连接 Nodejs 和 Mysql
- c# - 无法获得其中包含数组名称的二维数组的长度
- javascript - 将整数转换为 HH:MM:SS
- css - 如何解决搜索框的 CSS 宽度问题
- javascript - 如何从区间回调中访问创建区间的对象?
- javascript - 如何在另一个文件的函数中使用这个值