在JavaScript中检测CSS溢出

3
为了在网页上显示一行文本(比如在论坛中),如果文本超出了一行,不使用 CSS 的 overflow 属性截断,需要采取多种方式。我仍在寻求最佳解决方案。例如,
在 CSS 中添加 "..." 作为背景图像是一种可能的方法,但它会一直出现,这不是一个好的解决方案。
有些网站只计算字符数,如果超过 - 例如 - 100,就截断字符串以保留只有 100(或 97)个字符并在末尾添加 "..."。但是字体通常不是等宽的,所以结果并不美观。例如,在像素中占用空间的
  • "AAA" 和 "iii" 明显不同
  • "AAA""iii" 在比例字体下具有相同的宽度
  • 还有一个想法可以获得字符串的确切像素大小:
    • 在 JavaScript 中创建一个 DIV
    • 将文本插入其中(例如通过 innerHTML)
    • 测量宽度(通过 .offsetWidth)
    这还没有实现。然而,我想知道是否会有任何浏览器兼容性问题?
    有人尝试过这个解决方案吗?欢迎提出其他建议。

    2
    我喜欢使用DIV来标识字符串的宽度作为HTML元素。 - Chandu
    @Cybernate:DIV不是一个好的解决方案,因为它默认继承父元素的宽度,不允许其随内容而“增长”。你最好使用SPAN举个例子 - Brad Christie
    @Brad 在JavaScript中创建一个DIV而不将其附加到任何父元素上,这不是一个解决方案吗? - Déjà vu
    1
    不加入DOM树,宽度始终为0。可以自己试一下:var d = $('<div>');/*$('body').append(d);*/d.text('Hello,World!');var w1 = d.width();d.text('Longer string goes here');var w2 = d.width();alert('Short: '+w1+'\nLong: '+w2);(取消或添加注释[使用jQuery简化]) - Brad Christie
    3个回答

    3

    因此,我使用一些JS使其运行起来:http://jsfiddle.net/ug8Fc/3/

    基本上,它使用一个虚拟的 span 元素来计算“内容”的宽度(这应该允许使用几乎任何字体,因为 span 将相应地扩展)。然后,如果内容超出了容器,它会为省略号腾出空间,并不断删除字符直到适合为止。最后,它重新添加修改后的内容和省略号。

    如果有人有热情,也许可以将其制作成一个 jQuery 函数,以便更轻松地应用于任何选择器。

    <!DOCTYPE html>
    <html>
    <head>
      <meta http-equiv="content-type" content="text/html; charset=UTF-8">
      <title> - jsFiddle demo</title>
      <script type='text/javascript' src='http://code.jquery.com/jquery-1.4.4.min.js'></script>
      <link rel="stylesheet" type="text/css" href="/css/normalize.css">
      <link rel="stylesheet" type="text/css" href="/css/result-light.css">  
      <style type='text/css'>
        table {
          border: 1px solid #000;
          border-collapse: collapse;
          border-spacing: 0;
          width: 800px;
        }
        table thead {
          background-color: #AAA;
          color: #333;
        }
        table tbody tr {
          border: 1px solid #000;
        }
        table tbody td {
          padding: 2px;
        }
        .from {
          width: 100px;
        }
        .preview {
        }
        .date {
          width: 90px;
        }
      </style>
      <script type='text/javascript'>
      //<![CDATA[ 
      $(window).load(function(){
        // Create a span we can use just to test the widths of strings
        var spanTest = $('<span>').css('display','none').attr('id','span-test-tester');
        $('body').append(spanTest);
    
        // function to get the length of a string
        function getLength(txt){
            return spanTest.text(txt).width();
        }
    
        // now make all the previews fit
        $('table tbody .preview').each(function(){
            // build a bench-mark to gauge it from
            var roomWidth = $(this).innerWidth();
    
            // now, get the width and play with it until it fits (if it doesn't)
            var txt = $(this).text();
            var contentsWidth = getLength(txt);
            if (contentsWidth > roomWidth){ // bigger than we have to work with
                roomWidth -= getLength('...'); // work within confines of room + the ellipsis
                while (contentsWidth > roomWidth){ 
                    txt = txt.substring(0,txt.length-1);
                    contentsWidth = getLength(txt);
                }
                // set the text to this
                $(this).text(spanTest.text()).append($('<span>').text('...'));
            }
        });
    
      });
      //]]> 
      </script>
    </head>
    <body>
      <table>
        <thead>
          <tr>
            <th class="from">From</th>
            <th class="preview">Subject</th>
            <th class="date">Date</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td class="from">Joe Smith</td>
            <td class="preview">Hello bob, I just wanted to check in and see how things were going.
                I know we spoke last week about etc. etc. etc.</td>
            <td class="date">Dec 1, 2010</td>
          </tr>
          <tr>
            <td class="from">John Doe</td>
            <td class="preview">Hey bob, got any plans for new years yet?</td>
            <td class="date">Dec 28, 2010</td>
          </tr>
          <tr>
            <td class="from">Ben Franklin</td>
            <td class="preview">Happy New Year! Hope you had a great time at the casinos (and didn't
                spend too much money (haha)). Hope to see you this Memorial day blah blah blah
          </tr>
        </tbody>
      </table>
    </body>
    </html>
    

    谢谢。如果有其他人精力充沛,她可以在所有主要浏览器上测试它 :-) - Déjà vu
    @ring0:作为一条注释,我注意到在表格的某些宽度上,它会偏移几个像素(导致我有一个不想要的换行)。这是由于 roomWidth -= getLength('...'); 不足引起的。如果看起来很常见,请尝试将该行更改为 roomWidth -= getLength('...') + 5; 以给自己更多的缓冲空间。 - Brad Christie

    3

    不确定问题在您发布后是否更新,但目前该问题包括“(不使用CSS溢出属性截断)”。这意味着他们考虑过并且不想使用您的建议(尽管显然这是最简单的方法)。 - snuggles

    2

    这段代码真的帮了我很多,谢谢!

    但是我注意到还有一些需要改进的地方:

    1/ 解决某些情况下宽度不足的问题

    替换为:

    <td class="preview">Hey bob, got any plans for new years yet?</td>
    

    使用:

    <td><div class="preview">Hey bob, got any plans for new years yet?</div></td>
    

    2/ 脚本在使用特定字体(大小,粗细,家族等)时无法正常工作

    您需要将字体应用于用于计算文本大小的“不可见”span。例如(必要时应补充所有所需的字体属性):

        (...)
        // build a bench-mark to gauge it from
        var roomWidth = $(this).innerWidth();
    
        // now, get the width and play with it until it fits (if it doesn't)
        var fontSize=$(this).css("font-size");
        spanTest.css("font-size", fontSize);
        var fontStyle=$(this).css("font-style");
        spanTest.css("font-style", fontStyle);
        var fontWeight=$(this).css("font-weight");
        spanTest.css("font-weight", fontWeight);
        var fontFamily=$(this).css("font-family");
        spanTest.css("font-family", fontFamily);
        (...)
    

    3/ 性能

    我进行了一个测试,使用Firefox 7,其中有1000个元素,每个元素需要删除25个额外的字符。你的脚本需要近40秒才能完成工作。

    似乎问题出在“display:none”的span上。

    将该span放在窗口之外,使用“position:absolute;top:-100px”,可以提高性能:处理1000个元素只需要大约11秒!

    所以,请替换为:

    var spanTest = $('<span>').css('display','none').attr('id','span-test-tester');
    

    随着:

    var spanTest = $('<span>').attr('id','span-test-tester').css('position','absolute').css('top','-100px');
    

    总之:

    再次感谢这个脚本……它非常有用!

    下面是我完整的代码适配,如果有人需要的话……:

    <!DOCTYPE html>
    <html>
    <head>
      <meta http-equiv="content-type" content="text/html; charset=UTF-8">
      <title> - jsFiddle demo</title>
      <script type='text/javascript' src='http://code.jquery.com/jquery-1.4.4.min.js'></script>
      <link rel="stylesheet" type="text/css" href="/css/normalize.css">
      <link rel="stylesheet" type="text/css" href="/css/result-light.css">  
      <style type='text/css'>
    
        .preview {    
        }
    
        .backg{
          background:red;
          margin: 20px 0;
          border:1px solid blue;
          width:200px;
          padding:10px;
          font-size:14px;  // change it to test with other fonts
          font-weight:bold;
        }
    
      </style>
      <script type='text/javascript'>
      //<![CDATA[ 
      $(window).load(function(){
       truncate();
      });
    
    
      function truncate(){
    
        // Create a span we can use just to test the widths of strings
        var spanTest = $('<span>').attr('id','span-test-tester').css('position','absolute').css('top','-100px');
        $('body').append(spanTest);
    
        // function to get the length of a string
        function getLength(txt){
            return spanTest.text(txt).width();
        }
    
        var nb =0;
        // now make all the previews fit
        $('.preview').each(function(){
            nb++;
    
            // Get the current font and apply it to hidden span tester
            var fontSize=$(this).css("font-size");
            spanTest.css("font-size", fontSize);
            var fontStyle=$(this).css("font-style");
            spanTest.css("font-style", fontStyle);
            var fontWeight=$(this).css("font-weight");
            spanTest.css("font-weight", fontWeight);
            var fontFamily=$(this).css("font-family");
            spanTest.css("font-family", fontFamily);
    
            // build a bench-mark to gauge it from
            var roomWidth = $(this).innerWidth();
    
            // now, get the width and play with it until it fits (if it doesn't)
            var txt = $(this).text();
            var contentsWidth = getLength(txt);
            if (contentsWidth > roomWidth){ // bigger than we have to work with
                roomWidth -= getLength('...'); // work within confines of room + the ellipsis
                while (contentsWidth > roomWidth){ 
                    txt = txt.substring(0,txt.length-1);
                    contentsWidth = getLength(txt);
                }
    
                // set the text to this
                $(this).text(txt).append($('<span>').text('...'));
            }
        });
    
        }
    
      //]]> 
    
      </script>
    </head>
    <body>
          <div class="backg"><div class="preview">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</div></div>
          (... repeat 1000 times ...)
          <div class="backg"><div class="preview">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</div></div>
    
    </body>
    </html>
    

    再见!


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