开发者

CSS overflow detection in JavaScript

In order to display a line of text (like in a forum), and end that line with "..." if the text would overflow the line (not truncating using the CSS overflow property), there are a number of ways. I'm still seeking the best solution. For instance,

Adding in CSS a "..." as a background-image is a possibility, but it would appear all the time, and this is not a good solution.

Some sites just count the characters and if over - say - 100, truncates the string to keep only 100 (or 97) chars and add a "..." at the end. But fonts are usually not proportional, so the result is not pretty. For instance the space - in pixels - take开发者_JS百科n by

  • "AAA" and "iii" is clearly different
  • "AAA" and "iii" via a proportional font have the same width

There is another idea to get the exact size in pixels of the string:

  • create in Javascript a DIV
  • insert the text in it (via innerHTML for instance)
  • measure the width (via .offsetWidth)

which is not implemented yet. However, I wonder if there could be any browser compatibility problem?

Did any one tried this solution? Other recommendations would be welcome.


So, with a little JS I was able to get it working: http://jsfiddle.net/ug8Fc/3/

basically, it uses a dummy span that's hidden in the background to calculate the width of the "contents" (which should allow for almost any kind of font to be used as the span will extend accordingly). then if the contents exceed the container, it makes room for the ellipsis and keeps removing characters until it fits. Finally, it re-adds the modified contents and the ellipsis.

If someone gets energetic, maybe you can make it a jquery function that can be applied to any selector easier.

<!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>


This can be done easily with css using text-overflow: ellipsis. No need for hacky javascript.

http://www.w3schools.com/cssref/css3_pr_text-overflow.asp


This code really helped me, thanks !

But I noticed a few enhancements to do :

1/ To solves the problem of insufficient width in some cases

Replace :

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

With :

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

2/ The script doens't works well with specific fonts (size, weight, family...)

You need to apply the fonts to your "invisible" span used to compute the text size. For example (to complete if necessary with all needed font properties) :

    (...)
    // 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/ Performances

I made a test with 1000 elements, each with 25 extra characters to remove (on Firefox 7) You script needs near 40s to finish the job.

It seems that the problem is the "display:none" span.

Putting the span outside the window with "position:absolute;top:-100px" leads to better performances : about 11s to process the 1000 elements !

So replace :

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

With :

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

To conclude :

Thank you again for this script... It's very usefull !

Here my complete code adaptation, if it can help someone... :

<!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>

Bye !

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜