HTML文本溢出省略号检测

280

我的页面上有一些元素,它们的CSS规则为white-spaceoverflowtext-overflow,这样溢出的文本会被截断,并使用省略号。

div {
  white-space: nowrap;
  text-overflow: ellipsis;
  overflow: hidden;

  border: 1px solid black;
  width: 150px;
  margin: 5px;
  padding: 5px;
  font-family: sans-serif;
}
<div>
  <span>Normal text</span>
</div>
<div>
  <span>Long text that will be trimmed text</span>
</div>

有没有办法可以使用JavaScript检测哪些元素的内容溢出了?


15
不是重复问题!那个问题是关于一个元素内的另一个父元素。我讨论的是单个元素内的文本。在我的情况下,SPAN在TD内从不溢出,而是SPAN内的文本溢出并被修剪。这就是我想要检测的!抱歉 - 我承认我可能没有很好地提出这个问题。 - deanoj
哦,我忘记了添加 - 如果有帮助的话,这只需要在 Webkit 上工作... - deanoj
我只是试着用 Ghommey 来验证它是否可行……但没成功。 - deanoj
检测溢出的唯一需求是忽略省略号方面。 - Spudley
20个回答

436

尝试使用此JS函数,将元素作为参数传递:

function isEllipsisActive(e) {
     return (e.offsetWidth < e.scrollWidth);
}

19
这个答案和Alex的答案在IE8中不起作用;有一些奇怪的情况,滚动宽度和外部宽度是相同的...但它具有省略号,还有一些情况,它们是相同的...而且没有省略号。换句话说,第一个解决方案是唯一适用于所有浏览器的解决方案。 - user1026723
8
需要了解offsetWidth、scrollWidth和clientWidth的人,可以参考这篇非常好的解释: https://dev59.com/aWEi5IYBdhLWcg3wq9xD#21064102 - Linh Dam
12
当使用text-overflow:ellipsis时,应该满足条件e.offsetWidth <= e.scrollWidth。 - oriadam
5
我正在调查一个问题,似乎检测结果偏移了一个字符。我比应该多检测了一个字符的溢出。我认为当文本实际上可以适合时,显示省略号时可能存在轻微差异。我尝试暂时禁用“text-overflow: ellipsis”,果然,文本适合而没有任何溢出。可能是浏览器问题。我在Windows 10上使用Firefox 62。 - Ken Lyon
19
在flex子元素上它们总是相等的,因此对它们不起作用。 - Kevin Beal
显示剩余10条评论

137

从前,我需要做这件事情,但唯一可靠的跨浏览器解决方案是一个折中的方法。虽然我不是很喜欢这样的解决方案,但它确实每次都能正确地产生结果。

这个想法是克隆元素,移除任何限制宽度,并测试克隆元素是否比原始元素宽。如果是,那么您就知道它将被截断。

例如,使用jQuery:

var $element = $('#element-to-test');
var $c = $element
           .clone()
           .css({display: 'inline', width: 'auto', visibility: 'hidden'})
           .appendTo('body');

if( $c.width() > $element.width() ) {
    // text was truncated. 
    // do what you need to do
}

$c.remove();

我制作了一个jsFiddle来演示这个问题,http://jsfiddle.net/cgzW8/2/

甚至您可以为jQuery创建自己的自定义伪选择器:

$.expr[':'].truncated = function(obj) {
  var $this = $(obj);
  var $c = $this
             .clone()
             .css({display: 'inline', width: 'auto', visibility: 'hidden'})
             .appendTo('body');

  var c_width = $c.width();
  $c.remove();

  if ( c_width > $this.width() )
    return true;
  else
    return false;
};

然后使用它来查找元素

$truncated_elements = $('.my-selector:truncated');

演示: http://jsfiddle.net/cgzW8/293/

希望这会有所帮助,尽管有些hacky。


2
我必须说,在互联网上搜索并尝试实现许多解决方案之后,这是我发现的迄今为止最可靠的解决方案。与element.innerWidth或element.OffsetWidth不同,这个解决方案在使用边距或填充时不会在浏览器之间产生不同的结果。非常好的解决方案,干得好。 - Scription
1
对我来说,这在任何地方都不起作用。我不确定它与CSS有什么关系(我尝试在输入控件上使用它,下面的解决方案至少在Chrome和Firefox中有效(但在IE11中无效))... - Alexander
6
非常好的解决方案,尤其是使用jQuery伪选择器。但有时可能无法正常工作,因为如果元素文本具有不同的样式(字体大小、字母间距、内部元素的边距),则宽度计算会出现错误。在这种情况下,我建议将克隆元素附加到$this.parent()而不是body。这将提供元素的完全副本,计算将是正确的。无论如何,谢谢。 - Alex
1
这对我不起作用。我没有使用span,而是使用输入字段。有人知道是否需要进行任何更改才能使其适用于输入吗? - discodowney
1
将其附加到element.parentNode是否比将其附加到body更好?(某些影响宽度的css嵌套规则可能在将其附加到body时不适用) - Kevin
显示剩余20条评论

17

补充italo的回答,您还可以使用jQuery完成此操作。

function isEllipsisActive($jQueryObject) {
    return ($jQueryObject.width() < $jQueryObject[0].scrollWidth);
}

另外,正如 Smoky 指出的那样,你可能希望使用 jQuery 的 outerWidth() 而不是 width()。

function isEllipsisActive($jQueryObject) {
    return ($jQueryObject.outerWidth() < $jQueryObject[0].scrollWidth);
}

16

对于那些正在使用(或计划使用)Christian Varga的答案的人,请注意性能问题。

这种方式克隆/操作DOM会导致DOM回流(请参见此处的DOM回流解释),这是极其耗费资源的。

在页面上使用Christian Varga的解决方案在100多个元素上导致了4秒的回流延迟,期间JS线程被锁定。考虑到JS是单线程的,这意味着对最终用户造成了重大的UX延迟。

Italo Borssatto的答案应该是被接受的,通过我的性能分析,它大约快了10倍。


4
很遗憾,它并不能在每种情况下都起作用(我希望它能)。你提到的性能问题可以通过仅在需要检查的情况下运行来抵消,比如只在 mouseenter 时运行以查看是否需要显示工具提示。 - Jacob

9
< p >来自italo的回答非常好!不过让我稍微完善一下:

function isEllipsisActive(e) {
   var tolerance = 2; // In px. Depends on the font you are using
   return e.offsetWidth + tolerance < e.scrollWidth;
}

跨浏览器兼容性

如果你尝试以上代码,并使用console.log打印出e.offsetWidthe.scrollWidth的值,你会发现在IE上,即使没有文字截断,也会有1px2px的差异。

因此,根据你使用的字体大小,应该允许一定的容差!


它在IE10上不起作用 - offsetWidth与scrollWidth相同,两者都给出了我被截断的宽度。 - Costin_T
1
我也需要在Chrome 60(最新版本)上具有这种容差。 - Jan Żankowski

7
这个示例展示了在文本被截断的单元格表格中显示工具提示。它是基于表格宽度动态调整的。
$.expr[':'].truncated = function (obj) {
    var element = $(obj);

    return (element[0].scrollHeight > (element.innerHeight() + 1)) || (element[0].scrollWidth > (element.innerWidth() + 1));
};

$(document).ready(function () {
    $("td").mouseenter(function () {
        var cella = $(this);
        var isTruncated = cella.filter(":truncated").length > 0;
        if (isTruncated) 
            cella.attr("title", cella.text());
        else 
            cella.attr("title", null);
    });
});

演示: https://jsfiddle.net/t4qs3tqs/

它适用于jQuery的所有版本。


太完美了!正是我所需要的。只有几个来自2023年的更新:不应该是$.expr[':'].truncated,而是$.expr.pseudos.truncated(自jQuery 3.0起),$(...).on('mouseenter', ...)和对单行代码$this.attr('title', isTruncated ? $this.text() : null)的小改进。 - undefined

5

elem.offsetWdith 和 ele.scrollWidth 的区别 这对我很有用! https://jsfiddle.net/gustavojuan/210to9p1/

$(function() {
  $('.endtext').each(function(index, elem) {
    debugger;
    if(elem.offsetWidth !== elem.scrollWidth){
      $(this).css({color: '#FF0000'})
    }
  });
});

1
当前的Chrome和Firefox都无法正常工作。两个元素都变成了红色。 - xehpuk

4
所有的解决方案对我都没有起作用,真正起作用的是将元素的scrollWidth与其父元素(或子元素,具体取决于哪个元素有触发器)的scrollWidth进行比较。
当子元素的scrollWidth高于其父元素时,意味着.text-ellipsis已被激活。
el是父元素时
function isEllipsisActive(el) {
    let width       = el.offsetWidth;
    let widthChild  = el.firstChild.offsetWidth;
    return (widthChild >= width);
}

el是子元素时
function isEllipsisActive(event) {
    let width       = el.offsetWidth;
    let widthParent = el.parentElement.scrollWidth;
    return (width >= widthParent);
}

4

我的实现

const items = Array.from(document.querySelectorAll('.item'));
items.forEach(item =>{
    item.style.color = checkEllipsis(item) ? 'red': 'black'
})

function checkEllipsis(el){
  const styles = getComputedStyle(el);
  const widthEl = parseFloat(styles.width);
  const ctx = document.createElement('canvas').getContext('2d');
  ctx.font = `${styles.fontSize} ${styles.fontFamily}`;
  const text = ctx.measureText(el.innerText);
  return text.width > widthEl;
}
.item{
  width: 60px;
  overflow: hidden;
  text-overflow: ellipsis;
}
      <div class="item">Short</div>
      <div class="item">Loooooooooooong</div>


2
补充一下@Дмытрык的回答,为了功能完善,还需要考虑边框和填充的扣除!

const items = Array.from(document.querySelectorAll('.item'));
items.forEach(item =>{
    item.style.color = checkEllipsis(item) ? 'red': 'black'
})

function checkEllipsis(el){
  const styles = getComputedStyle(el);
  const widthEl = parseFloat(styles.width);
  const ctx = document.createElement('canvas').getContext('2d');
  ctx.font = `${styles.fontSize} ${styles.fontFamily}`;
  const text = ctx.measureText(el.innerText);

  let extra = 0;
  extra += parseFloat(styles.getPropertyValue('border-left-width'));
  extra += parseFloat(styles.getPropertyValue('border-right-width'));
  extra += parseFloat(styles.getPropertyValue('padding-left'));
  extra += parseFloat(styles.getPropertyValue('padding-right'));
  return text.width > (widthEl - extra);
}
.item{
  width: 60px;
  overflow: hidden;
  text-overflow: ellipsis;
}
      <div class="item">Short</div>
      <div class="item">Loooooooooooong</div>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接