传统解决方案通常通过比较元素的 scrollWidthclientWidth 来判断文本是否被截断。

此外,我们可以使用 Range 的方式更精确地判断文本是否被截断。

  • overflow: hidden 在布局上会将文本进行截断,但是双击全选复制的时候,可以复制到全部的内容。因此我们可以基于此特性,通过 浏览器提供的 Range api 获取 文本的宽度/高度进行判断。
const app = document.getElementById('app')
  
  const range = document.createRange()
  range.setStart(app, 0)
  range.setEnd(app, app.childNodes.length)
  
  const { width: rangeWidth, height: rangeHeight } =
    range.getBoundingClientRect()
  
  const { width, height } = target.getBoundingClientRect()

  if (rangeWidth > width || rangeHeight > height) {
		  // 文本被截断 执行的逻辑
  }
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.

何时使用 Range

正常布局下,Range 都可以使用 scrollWidth 的方式平替。

当布局出现异常布局偏移时,scrollWidth 可能无法准确判断文本是否被截断。

异常布局偏移:正常文字的排列方向都是从左往右,右区域超出的部分被 hidden 截断,此时的 scrollWidth 会包括 hidden 的部分宽度,

如果使用 css 的一些属性使得文字排列从左开始就已经被截断了一部分,最开始截断的部分是不会算在 scrollWidth 中。

简单总结:如果文本排列是从左到右布局,右边被截断的部分会算在 scrollWidth 中,而左边被截断的部分不会。

举个例子:如果元素使用了 text-indent: -50px; 的方式进行了负缩进而被隐藏了。

.text-container {
      width: 200px;
      overflow: hidden;
      border: 1px solid #000;
    }
    .text-container p {
      display: inline-block;
      text-indent: -50px; /* 负缩进 */
      white-space: nowrap;
    }
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
<div class="text-container" id="text">
    <p>这是一个带有负缩进的长文本。</p>
  </div>
  
  <button onclick="checkScrollWidth()">使用 scrollWidth 检测</button>
  <button onclick="checkRange()">使用 createRange 检测</button>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
function checkScrollWidth() {
      const textElement = document.getElementById('text');
      if (textElement.scrollWidth > textElement.clientWidth) {
        console.log('文本被截断了 (scrollWidth 检测)');
      } else {
        console.log('文本未被截断 (scrollWidth 检测)');
      }
    }

    function checkRange() {
      const textElement = document.getElementById('text');
      const range = document.createRange();
       range.setStart(textElement, 0)
       range.setEnd(textElement, textElement.childNodes.length) // 选择 p 标签内容

      const rangeWidth = range.getBoundingClientRect().width;
      if (rangeWidth > textElement.clientWidth) {
        console.log('文本被截断了 (createRange 检测)');
      } else {
        console.log('文本未被截断 (createRange 检测)');
      }
    }
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.

此时两者的判断结果是有差异的,由于布局向左偏移了 50px ,没有造成滚动条,因此scrollWidth 的方式判断为文本没有截断。而range则包含了左边截断的部分的宽度。