Thursday, May 5, 2011

String truncation in the middle, Mac style, CSS and jQuery

Hello!

I need to implement a certain kind of string truncation where a string splits in the middle and its left part becomes resizable. Here is what it might look like:

"Lorem ipsum dolor sit amet. Ut ornare dignissim ligula sed commodo."

becomes

"Lorem ipsum dolor sit amet ... commodo."

I am open to solutions using pure CSS or with some help of JavaScript/jQuery. So far I have tried CSS, but it did not give me the exact result I wanted. For one, I would like a string, once stretched out fully [in a table cell], so there's no need for truncation anymore, to lose the omission points ("...") in the middle and display normally.

Here is a pic to demonstrate how it's done in OS X: http://i40.tinypic.com/29yqslf.jpg

Thanks.

From stackoverflow
  • The following Javascript function will do a middle truncation, like OS X:

    function smartTrim(string, maxLength) {
        if (!string) return string;
        if (maxLength < 1) return string;
        if (string.length <= maxLength) return string;
        if (maxLength == 1) return string.substring(0,1) + '...';
    
        var midpoint = Math.ceil(string.length / 2);
        var toremove = string.length - maxLength;
        var lstrip = Math.ceil(toremove/2);
        var rstrip = toremove - lstrip;
        return string.substring(0, midpoint-lstrip) + '...' + string.substring(midpoint+rstrip);
    

    It will replace characters in the middle with ellipsis. My unit tests show:

    var s = '1234567890';
    assertEquals(smartTrim(s, -1), '1234567890');
    assertEquals(smartTrim(s, 0), '1234567890');
    assertEquals(smartTrim(s, 1), '1...');
    assertEquals(smartTrim(s, 2), '1...0');
    assertEquals(smartTrim(s, 3), '1...90');
    assertEquals(smartTrim(s, 4), '12...90');
    assertEquals(smartTrim(s, 5), '12...890');
    assertEquals(smartTrim(s, 6), '123...890');
    assertEquals(smartTrim(s, 7), '123...7890');
    assertEquals(smartTrim(s, 8), '1234...7890');
    assertEquals(smartTrim(s, 9), '1234...67890');
    assertEquals(smartTrim(s, 10), '1234567890');
    assertEquals(smartTrim(s, 11), '1234567890');
    
    Vasily : This is great and works as described, however I have a need to make the output string "stretchable", so that the truncation is determined based on one's viewport size. Huge screen—no truncation, narrow screen—string is truncated using a dynamically calculated number of characters for best fit.
    johnvey : You'll have to attach an event handler to its container, which will then calculate the appropriate maxLength in relation to the container width property. If your font-family is monospaced, this will be easy; if proportional, then you'll need to experiment to get the right heuristic.
  • You can't do that with CSS. The problem is that HTML and CSS are supposed to work in a variety of browsers and fonts and it is almost impossible to calculate the width of a string in a consistent way. This is an idea that might help you. However, you would need to do that a number of times, until you find the string with the appropriate width.

  • In the template engine put the real value in an custom tag like

    <span original="your string here"></span>
    

    Then put an onload and an onresize events to a javascript function which will read the original attribute and place it in the innerHTML of your span tag. Here is an example of the truncation function:

    function start_and_end(str) {
      if (str.length > 35) {
        return str.substr(0, 20) + '...' + str.substr(str.length-10, str.length);
      }
      return str;
    }
    

    Adjust the values or possible make them dynamic if necessary for different objects. If you have users from different browsers you can steal a reference width from a text by the same font and size elsewhere in your dom. Then interpolate to an appropriate amount of characters to use.

    A tip is also to have an abbr-tag on the ... or who message to make the user be able to get a tooltip with the full string.

    <abbr title="simple tool tip">something</abbr>
    
    Vasily : This ended up being the best basis for my final solution. With a few extra lines of JavaScript code it does exactly what I wanted it to do. I have decided to use "title" instead of the custom tag so now it provides accessibility naturally. Thanks Stefan, John, and Kgiannakakis!

0 comments:

Post a Comment