【问题标题】:HTML text-overflow ellipsis detectionHTML文本溢出省略号检测
【发布时间】:2011-12-05 23:46:45
【问题描述】:

我在页面上有一组块元素。它们都设置了 CSS 规则 white-space、overflow、text-overflow,以便修剪溢出的文本并使用省略号。

但是,并不是所有的元素都会溢出。

无论如何我可以使用 javascript 来检测哪些元素溢出?

谢谢。

添加:我正在使用的示例 HTML 结构。

<td><span>Normal text</span></td>
<td><span>Long text that will be trimmed text</span></td>

SPAN 元素始终适合单元格,它们应用了省略号规则。我想检测省略号何时应用于 SPAN 的文本内容。

【问题讨论】:

  • 不是重复的!这个问题是关于另一个父元素中的一个元素。我说的是单个元素中的文本。就我而言,TD 中的 SPAN 永远不会溢出 TD,而是 SPAN 中的文本溢出并被修剪。这就是我想要检测的!抱歉 - 我承认我本来可以更好地提出这个问题。
  • 哦,我忘了补充——如果有帮助的话,这只需要在 webkit 上工作......
  • 我做了 Ghommey 只是为了看看它是否有效......它没有。
  • 省略号方面无关;你只需要检测它是否溢出。

标签: javascript html css ellipsis


【解决方案1】:

如果你要做出反应,我就是这样做的。

<div 
  ref={ref => {
    if (!ref) return
    const isOverflowing = ref.scrollWidth > ref.clientWidth
    if (isOverflowing) {
      // handle what to do next here
    }
  }}
/>

【讨论】:

    【解决方案2】:

    没有一个解决方案适合我,所以我选择了一种完全不同的方法。我没有使用带有省略号的 CSS 解决方案,而是从特定的字符串长度中剪切文本。

      if (!this.isFullTextShown && this.text.length > 350) {
        return this.text.substring(0, 350) + '...'
      }
      return this.text
    

    如果超过长度,则显示“更多/更少”按钮。

      <span
        v-if="text.length > 350"
        @click="isFullTextShown = !isFullTextShown"
      >
        {{ isFullTextShown ? 'show less' : 'show more' }}
      </span>
    

    【讨论】:

    • 爱这个事实 10 年后这个问题仍然有效 - 尽管我怀疑 10 年后浏览器标准有所改善! :-)
    【解决方案3】:

    所有的解决方案都没有真正为我工作,所做的工作是将元素 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】:

      https://stackoverflow.com/users/241142/iconoclast提到的演示http://jsfiddle.net/brandonzylstra/hjk9mvcy/中存在一些错误。

      在他的演示中,添加这些代码将起作用:

      setTimeout(() => {      
        console.log(EntryElm[0].offsetWidth)
      }, 0)
      

      【讨论】:

        【解决方案5】:

        我的实现)

        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>

        【讨论】:

          【解决方案6】:

          @ItaloBorssatto 的解决方案非常完美。但在看之前 - 我做出了决定。这是:)

          const elems = document.querySelectorAll('span');
          elems.forEach(elem => {
            checkEllipsis(elem);
          });
          
          function checkEllipsis(elem){
            const canvas = document.createElement('canvas');
            const ctx = canvas.getContext('2d');
            const styles = getComputedStyle(elem);
            ctx.font = `${styles.fontWeight} ${styles.fontSize} ${styles.fontFamily}`;
            const widthTxt = ctx.measureText(elem.innerText).width;
            if (widthTxt > parseFloat(styles.width)){
              elem.style.color = 'red'
            }
          }
          span.cat {
              display: block;
              border: 1px solid black;
              white-space: nowrap;
              width: 100px;
              overflow: hidden;
              text-overflow: ellipsis;
          }
           <span class="cat">Small Cat</span>
                <span class="cat">Looooooooooooooong Cat</span>

          【讨论】:

            【解决方案7】:

            e.offsetWidth &lt; e.scrollWidth 解决方案并不总是有效。

            如果你想使用纯 JavaScript,我建议使用这个:

            (打字稿)

            public isEllipsisActive(element: HTMLElement): boolean {
                element.style.overflow = 'initial';
                const noEllipsisWidth = element.offsetWidth;
                element.style.overflow = 'hidden';
                const ellipsisWidth = element.offsetWidth;
            
                if (ellipsisWidth < noEllipsisWidth) {
                  return true;
                } else {
                  return false;
                }
            }
            

            【讨论】:

            • 需要对每个元素进行重排可能效率很低,就像您在表格中的数百个单元格中使用它一样。
            【解决方案8】:

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

            以这种方式克隆/操作 DOM 会导致 DOM 回流(请参阅此处的 an explanation on DOM reflow),这是非常耗费资源的。

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

            Italo Borssatto 的 answer 应该是被接受的,它在我的分析过程中快了大约 10 倍。

            【讨论】:

            • 不幸的是,它并不适用于所有场景(我希望它会)。您在此处提到的性能损失可以通过仅在您需要检查的情况下运行来抵消 - 仅在 mouseenter 上查看是否需要显示工具提示。
            【解决方案9】:

            我认为更好的检测方法是使用getClientRects(),似乎每个矩形都有相同的高度,所以我们可以用不同的top值的数量来计算行数。

            getClientRectsthis 一样工作

            function getRowRects(element) {
                var rects = [],
                    clientRects = element.getClientRects(),
                    len = clientRects.length,
                    clientRect, top, rectsLen, rect, i;
            
                for(i=0; i<len; i++) {
                    has = false;
                    rectsLen = rects.length;
                    clientRect = clientRects[i];
                    top = clientRect.top;
                    while(rectsLen--) {
                        rect = rects[rectsLen];
                        if (rect.top == top) {
                            has = true;
                            break;
                        }
                    }
                    if(has) {
                        rect.right = rect.right > clientRect.right ? rect.right : clientRect.right;
                        rect.width = rect.right - rect.left;
                    }
                    else {
                        rects.push({
                            top: clientRect.top,
                            right: clientRect.right,
                            bottom: clientRect.bottom,
                            left: clientRect.left,
                            width: clientRect.width,
                            height: clientRect.height
                        });
                    }
                }
                return rects;
            }
            

            getRowRectsthis 一样工作

            你可以像this这样检测

            【讨论】:

              【解决方案10】:

              elem.offsetWdith VS ele.scrollWidth 这对我有用! https://jsfiddle.net/gustavojuan/210to9p1/

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

              【讨论】:

              • 在当前的 Chrome 和 Firefox 中都不起作用。两个元素都变为红色。
              【解决方案11】:

              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 上,即使您没有文本截断,a经历了1px2px 的价值差异。

              因此,根据您使用的字体大小,允许一定的公差!

              【讨论】:

              • 它在 IE10 上不起作用 - offsetWidth 与 scrollWidth 相同,都给了我截断的宽度。
              • 我在 Chrome 60(最新)上也需要这个容差。
              【解决方案12】:

              此示例在单元格表上显示工具提示,其中文本被截断。基于表格宽度是动态的:

              $.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

              【讨论】:

                【解决方案13】:

                曾几何时,我需要这样做,而我遇到的唯一跨浏览器可靠的解决方案是 hack 工作。我不是这样的解决方案的最大粉丝,但它肯定会一次又一次地产生正确的结果。

                这个想法是克隆元素,删除任何边界宽度,并测试克隆的元素是否比原始元素更宽。如果是这样,您就知道它会被截断。

                例如,使用 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/

                希望这会有所帮助,尽管它很老套。

                【讨论】:

                • @Lauri 不; CSS 截断不会更改框中的 实际 文本,因此内容始终相同,无论是截断、可见还是隐藏。仍然没有办法以编程方式获取截断的文本,如果有那么你就不需要首先克隆元素!
                • 好像这在没有white-space: nowrap的情况下是行不通的。
                • 我必须说,在互联网上搜索了很多并尝试实施许多解决方案后,这是迄今为止我发现的最可靠的一个。此解决方案不会在 element.innerWidth 或 element.OffsetWidth 等浏览器之间给出不同的结果,这在使用边距或填充时会出现问题。很好的解决方案,干得好。
                • 对我来说,这在任何地方都不起作用。我不确定,它如何依赖 CSS(我尝试在输入控件上使用它,下面的解决方案至少在 Chrome 和 Firefox 中有效(但在 IE11 中无效))...
                • 很好的解决方案,尤其是使用 jQuery 伪选择器。但有时可能不起作用,因为如果元素文本具有不同的样式(字体大小、字母间距、内部元素的边距),则会错误地计算宽度。在这种情况下,我建议将克隆元素附加到 $this.parent() 而不是“body”。这将提供元素的精确副本,并且计算将是正确的。还是谢谢
                【解决方案14】:

                添加到 italo 的答案,你也可以使用 jQuery 来做到这一点。

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

                此外,正如 Smoky 所指出的,您可能希望使用 jQuery outerWidth() 而不是 width()。

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

                【讨论】:

                  【解决方案15】:

                  试试这个 JS 函数,将 span 元素作为参数传递:

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

                  【讨论】:

                  • 这应该是答案 - 非常优雅,适用于 Chrome 和 IE(9)
                  • 这个答案和亚历克斯的答案在 IE8 中不起作用;在某些奇怪的情况下,滚动宽度和外部宽度相同……但它有省略号,然后在某些情况下它们相同……并且没有省略号。换句话说,第一个解决方案是唯一适用于所有浏览器的解决方案。
                  • 对于需要了解offsetWidth、scrollWidth、clientWidth的朋友,这里有一个很好的解释:stackoverflow.com/a/21064102/1815779
                  • text-overflow:ellipsis 上应该是e.offsetWidth &lt;= e.scrollWidth
                  • 它们在 flex 子节点上总是相等的,因此对它们不起作用。
                  猜你喜欢
                  • 1970-01-01
                  • 2013-01-16
                  • 1970-01-01
                  • 2018-07-22
                  • 1970-01-01
                  • 2013-04-01
                  相关资源
                  最近更新 更多