如何使用 Javascript 检查文本是否被 CSS 截断 [英] How to check if text is truncated by CSS using Javascript

查看:34
本文介绍了如何使用 Javascript 检查文本是否被 CSS 截断的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用 JS 检测文本是否被截断.此处提到的解决方案 效果很好,除了下面的边缘情况.您会注意到,如果文本在视觉上被截断,鼠标悬停时的第一个块将返回 false.

function isEllipsisActive(e) {return (e.offsetWidth 

div.red {底边距:1em;背景:红色;颜色:#fff;空白:nowrap;溢出:隐藏;文本溢出:省略号;宽度:300px;光标:指针;}

<h6>悬停鼠标并观察控制台消息.</h6><!-- 应该返回真--><div class="red" onmouseover="onMouseHover(this)"><a>分析报告出现乱码.请稍等</a>

<!-- 应该返回真--><div class="red" onmouseover="onMouseHover(this)"><a>分析报告出现乱码.Plssssss

<!-- 应该返回 false --><div class="red" onmouseover="onMouseHover(this)"><a>普通文本</a>

我所追求的解决方案是,只要文本被 css 截断,该函数就返回 true.

解决方案

这里的问题是 HTMLElement.offsetWidthElement.scrollWidth 都是四舍五入的值.
在我的计算机上,您元素的真正内部宽度实际上是 300.40625px,而在我的 Chrome 中,它被限制为 300px.

这里的解决方案是使用返回浮点值的 API,并没有太多...

人们可能会想检查内部 <a>getBoundingClientRect().width,这实际上适用于所有 OP 的情况,但仅适用于以下情况:向div、这些 <a> 的边距或其他元素,但它已损坏.

document.querySelectorAll( ".test" ).forEach( el => {el.classList.toggle( "截断", isEllipsisActive( el ) );});函数 isEllipsisActive( el ) {返回 el.firstElementChild.getBoundingClientRect().width >el.getBoundingClientRect().width;}

div.test {底边距:1em;背景:红色;颜色:#fff;空白:nowrap;溢出:隐藏;文本溢出:省略号;宽度:300px;}div.截断{背景:绿色;}.margin-left {左边距:225px;}

<div class="test"><a>分析报告出现乱码.请稍等</a>

<!-- 应该是绿色的--><div class="test"><a>分析报告出现乱码.Plssssss

<!-- 应该是绿色的--><div class="test"><a>分析</a><a>报告通过乱码.Plssssss

<!-- 应该是绿色的--><div class="test"><a class="margin-left">短文本</a>

<!-- 应该是红色的--><div class="test"><a>普通文本</a>

所以人们可能会认为范围及其getBoundingClientRect() 方法可以,然而,虽然这能够告诉您元素中文本内容的实际大小,但这只检查文本内容.如果滚动是由边距引起的,它将不起作用.

document.querySelectorAll(".test").forEach( el => {el.classList.toggle( "截断", isEllipsisActive( el ) );});函数 isEllipsisActive( el ) {返回 el.scrollWidth !== el.offsetWidth ?el.scrollWidth >el.offsetWidth :检查范围( el );//Blink 和 Webkit 浏览器做地板 scrollWidth}功能检查范围(el){const range = new Range();range.selectNodeContents(el);const range_rect = range.getBoundingClientRect();const el_rect = el.getBoundingClientRect();//假设 ltr 方向返回 range_rect.right >el_rect.right;}

div.test {底边距:1em;背景:红色;颜色:#fff;空白:nowrap;溢出:隐藏;文本溢出:省略号;宽度:300px;}div.截断{背景:绿色;}.margin-left {左边距:225px;}.边距右{右边距:225px;}

<div class="test"><a>分析报告出现乱码.请稍等</a>

<!-- 应该是绿色的--><div class="test"><a>分析报告出现乱码.Plssssss

<!-- 应该是绿色的--><div class="test"><a>分析</a><a>报告通过乱码.Plssssss

<!-- 应该是绿色的--><div class="test"><a class="margin-left">短文本</a>

<!-- 应该是绿色的--><div class="test"><a class="margin-right">短文本</a>

<!-- 应该是红色的--><div class="test"><a>普通文本</a>

所以我能想到的唯一解决方案依赖于 Chrome 特定的行为:他们确实在 Range.getClientRects().
因此,在 Chrome 中,确定是否呈现省略号的方法是切换 text-overflow 属性并检查此 DOMRect 是否出现.

然而,由于这是 Chrome 独有的行为,我们仍然需要检查 Safari 的 Range 的边界框位置.

document.querySelectorAll(".test").forEach( el => {el.classList.toggle( "截断", isEllipsisActive( el ) );});函数 isEllipsisActive( el ) {返回 el.scrollWidth !== el.offsetWidth ?el.scrollWidth >el.offsetWidth :检查范围( el );//Blink 和 Webkit 浏览器做地板 scrollWidth}功能检查范围(el){const range = new Range();range.selectNodeContents(el);const range_rect = range.getBoundingClientRect();const el_rect = el.getBoundingClientRect();//假设 ltr 方向if( range_rect.right > el_rect.right ) {返回真;}//以下检查对于 Blink 浏览器就足够了//但他们是唯一暴露这种行为的人.//第一个强制省略号el.classList.add( "text-overflow-ellipsis" );//获取所有客户端矩形(省略号应该有一个)const rects_ellipsis = range.getClientRects();//强制没有省略号el.classList.add( "text-overflow-clip" );const rects_clipped = range.getClientRects();//干净的el.classList.remove( "text-overflow-ellipsis" );el.classList.remove( "text-overflow-clip" );//如果计数改变,文本被截断返回 rects_clipped.length !== rects_ellipsis.length;}

/* 2 个新类强制省略号的渲染 */.text-overflow-省略号{文本溢出:省略号!重要;}.text-overflow-clip {文本溢出:剪辑!重要;}div.test {底边距:1em;背景:红色;颜色:#fff;空白:nowrap;溢出:隐藏;文本溢出:省略号;宽度:300px;}div.截断{背景:绿色;}.margin-left {左边距:225px;}.边距右{右边距:225px;}

<div class="test"><a>分析报告出现乱码.请稍等</a>

<!-- 应该是绿色的--><div class="test"><a>分析报告出现乱码.Plssssss

<!-- 应该是绿色的--><div class="test"><a>分析</a><a>报告通过乱码.Plssssss

<!-- 应该是绿色的--><div class="test"><a class="margin-left">短文本</a>

<!-- 应该是绿色的--><div class="test"><a class="margin-right">短文本</a>

<!-- 应该是红色的--><div class="test"><a>普通文本</a>

I am trying to detect if the text is truncated using JS. The solution mentioned here works great except for an edge case below. As you will notice, the first block on mouse hover will return false if though the text is visually truncated.

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

function onMouseHover(e) {
  console.log(`is truncated: ${isEllipsisActive(e)}`);
}

div.red {
  margin-bottom: 1em;
  background: red;
  color: #fff;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  width: 300px;
  cursor: pointer;
}

<h6>Hover mouse and watch for console messages.</h6>

<!-- should return true -->
<div class="red" onmouseover="onMouseHover(this)">
  <a>Analytics reports comes through garbled. Plsss</a>
</div>

<!-- should return true -->
<div class="red" onmouseover="onMouseHover(this)">
  <a>Analytics reports comes through garbled. Plsssssss</a>
</div>

<!-- should return false -->
<div class="red" onmouseover="onMouseHover(this)">
  <a>Normal text</a>
</div>

The solution I am after is for the function to return true whenever the text is truncated by css.

解决方案

The problem here is that both HTMLElement.offsetWidth and Element.scrollWidth are rounded values.
Your element's true inner-width is actually 300.40625px on my computer, and this gets floored to 300px in my Chrome.

The solution here is to use APIs that return float values, and there aren't much...

One could be tempted to check the inner <a>'s getBoundingClientRect().width, and that would actually work in all OP's cases, but that would only work in these case: Add a padding to the div, a margin to these <a>, or an other element and it's broken.

document.querySelectorAll( ".test" ).forEach( el => {
  el.classList.toggle( "truncated", isEllipsisActive( el ) );
} );

function isEllipsisActive( el ) {
  return el.firstElementChild.getBoundingClientRect().width > el.getBoundingClientRect().width;
}

div.test {
  margin-bottom: 1em;
  background: red;
  color: #fff;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  width: 300px;
}
div.truncated {
  background: green;
}
.margin-left {
  margin-left: 225px;
}

<!-- should be green -->
<div class="test">
  <a>Analytics reports comes through garbled. Plsss</a>
</div>

<!-- should be green -->
<div class="test">
  <a>Analytics reports comes through garbled. Plsssssss</a>
</div>

<!-- should be green -->
<div class="test">
  <a>Analytics</a><a> reports comes through garbled. Plsssssss</a>
</div>

<!-- should be green -->
<div class="test">
  <a class="margin-left">Shorter text</a>
</div>

<!-- should be red -->
<div class="test">
  <a>Normal text</a>
</div>

So one might think a Range and its getBoundingClientRect() method would do, however, while this is able to tell the real size of the text content in your element, this only checks for the text content. If the scrolling is caused by a margin, it won't work.

document.querySelectorAll(".test").forEach( el => {
    el.classList.toggle( "truncated", isEllipsisActive( el ) );
} );

function isEllipsisActive( el ) {
  return el.scrollWidth !== el.offsetWidth ?
    el.scrollWidth > el.offsetWidth :
    checkRanges( el ); // Blink and Webkit browsers do floor scrollWidth
}

function checkRanges( el ) {
  const range = new Range();
  range.selectNodeContents( el );
  
  const range_rect = range.getBoundingClientRect();
  const el_rect = el.getBoundingClientRect();
  // assumes ltr direction
  return range_rect.right > el_rect.right;
}

div.test {
  margin-bottom: 1em;
  background: red;
  color: #fff;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  width: 300px;
}
div.truncated {
  background: green;
}
.margin-left {
  margin-left: 225px;
}

.margin-right {
  margin-right: 225px;
}

<!-- should be green -->
<div class="test">
  <a>Analytics reports comes through garbled. Plsss</a>
</div>

<!-- should be green -->
<div class="test">
  <a>Analytics reports comes through garbled. Plsssssss</a>
</div>

<!-- should be green -->
<div class="test">
  <a>Analytics</a><a> reports comes through garbled. Plsssssss</a>
</div>

<!-- should be green -->
<div class="test">
  <a class="margin-left">Shorter text</a>
</div>

<!-- should be green -->
<div class="test">
  <a class="margin-right">Shorter text</a>
</div>

<!-- should be red -->
<div class="test">
  <a>Normal text</a>
</div>

So the only solution I could think of relies on a Chrome specific behavior: They do expose the Client Rect of the rendered ellipsis in the result of Range.getClientRects().
So a way to know for sure, in Chrome, if the ellipsis is rendered, is to toggle the text-overflow property and check if this DOMRect appeared.

However, since this is a Chrome only behavior, we still need to check for the Range's bounding-box position for Safari.

document.querySelectorAll(".test").forEach( el => {
    el.classList.toggle( "truncated", isEllipsisActive( el ) );
} );

function isEllipsisActive( el ) {
  return el.scrollWidth !== el.offsetWidth ?
    el.scrollWidth > el.offsetWidth :
    checkRanges( el ); // Blink and Webkit browsers do floor scrollWidth
}

function checkRanges( el ) {
  const range = new Range();
  range.selectNodeContents( el );
  
  const range_rect = range.getBoundingClientRect();
  const el_rect = el.getBoundingClientRect();
  // assumes ltr direction
  if( range_rect.right > el_rect.right ) {
    return true;
  }
  // Following check would be enough for Blink browsers
  // but they are the only ones exposing this behavior.
  
  // first force ellipsis
  el.classList.add( "text-overflow-ellipsis" );
  // get all the client rects (there should be one for the ellipsis)
  const rects_ellipsis = range.getClientRects();
  // force no ellipsis
  el.classList.add( "text-overflow-clip" );
  const rects_clipped = range.getClientRects();
  // clean
  el.classList.remove( "text-overflow-ellipsis" );
  el.classList.remove( "text-overflow-clip" );
  // if the counts changed, the text is truncated
  return rects_clipped.length !== rects_ellipsis.length;
}

/* 2 new clasess to force the rendering of ellipsis */
.text-overflow-ellipsis {
  text-overflow: ellipsis !important;
}
.text-overflow-clip {
  text-overflow: clip !important;
}

div.test {
  margin-bottom: 1em;
  background: red;
  color: #fff;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  width: 300px;
}
div.truncated {
  background: green;
}
.margin-left {
  margin-left: 225px;
}
.margin-right {
  margin-right: 225px;
}

<!-- should be green -->
<div class="test">
  <a>Analytics reports comes through garbled. Plsss</a>
</div>

<!-- should be green -->
<div class="test">
  <a>Analytics reports comes through garbled. Plsssssss</a>
</div>

<!-- should be green -->
<div class="test">
  <a>Analytics</a><a> reports comes through garbled. Plsssssss</a>
</div>

<!-- should be green -->
<div class="test">
  <a class="margin-left">Shorter text</a>
</div>

<!-- should be green -->
<div class="test">
  <a class="margin-right">Shorter text</a>
</div>

<!-- should be red -->
<div class="test">
  <a>Normal text</a>
</div>

这篇关于如何使用 Javascript 检查文本是否被 CSS 截断的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆