从“…”到精准截断:JS判断溢出位置的性能优化实战

187 阅读1分钟

背景需求。开发一个需要显示多行文本的网页或应用,当文本过长时,希望知道文本在何处溢出,以便进行截断或显示“更多”按钮。

需求1:通常,单行文本溢出可以用text-overflow: ellipsis

需求2:但多行的话可能需要结合-webkit-line-clamp

需求3:需要知道具体哪一行开始溢出,或者最后一个可见字符的位置。这时候可能需要更精确的方法,比如逐行测量文本的高度,直到超过容器的高度:。 方案:可能需要在隐藏的临时元素中插入文本,逐步调整文本长度,直到找到溢出点。


需求1:使用 CSS text-overflow: ellipsis

.text-container {
  text-overflow: ellipsis
}

需求2:使用 CSS line-clamp

如果只需要视觉上的省略号效果,推荐纯 CSS 方案:

.text-container {
  display: -webkit-box;
  -webkit-line-clamp: 3; /* 最多显示3行 */
  -webkit-box-orient: vertical;
  overflow: hidden;
}

需求3: 逐行计算

  1. 逐行计算:通过动态调整文本长度,找到刚好不溢出的临界点,如果需要知道具体从第几行开始溢出,可以通过动态插入文本逐行计算:

实现代码

function checkTextOverflow(element, text) {
  const lineHeight = parseInt(getComputedStyle(element).lineHeight);
  const maxHeight = element.clientHeight; // 容器可视高度
  const maxLines = Math.floor(maxHeight / lineHeight); // 容器最大行数
​
  // 创建一个临时隐藏的副本元素
  const tempElement = element.cloneNode();
  tempElement.style.position = 'absolute';
  tempElement.style.visibility = 'hidden';
  tempElement.style.width = element.clientWidth + 'px';
  tempElement.style.whiteSpace = 'pre-wrap'; // 保留换行符
  document.body.appendChild(tempElement);
​
  let currentText = '';
  let currentLines = 0;
  let overflowIndex = -1;
​
  // 逐字符插入,检测行数
  for (let i = 0; i < text.length; i++) {
    currentText += text[i];
    tempElement.textContent = currentText;
​
    const currentHeight = tempElement.scrollHeight;
    const newLines = Math.floor(currentHeight / lineHeight);
​
    if (newLines > maxLines) {
      overflowIndex = i; // 记录溢出位置
      break;
    }
  }
​
  document.body.removeChild(tempElement);
  return overflowIndex; // 返回溢出字符的索引(-1表示未溢出)
}
​
// 使用示例
const element = document.getElementById('text-container');
const text = element.textContent;
const overflowIndex = checkTextOverflow(element, text);