Ellipsis in the middle of a text (Mac style)

I need to implement ellipsis ("...") in the middle of a text within a resizable element. Here is what it might look like. So,

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

becomes

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

When the element is stretched out to the width of the text, I want the ellipsis to disappear. How can this be done?


Solution 1:

In the HTML, put the full value in a custom data-* attribute like

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

Then assign load and resize event listeners to a JavaScript function which will read the original data attribute and place it in the innerHTML of your span tag. Here is an example of the ellipsis 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 if 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>

Solution 2:

I'd like to propose mine example of solving this problem.

The main idea is to split text in two even parts (or first is bigger, if the length is odd) one of which has ellipsis in the end and another aligned right with text-overflow: clip.

So all you need to do with js, if you want to make it automatic/universal - is to split string and set attributes.

It has some disadvantages, though.

  1. No nice wrapping by words, or even letters (text-overflow: '' works only in FF at the moment)
  2. If the split happens between words - space should be in the first part. Otherwise, it will be collapsed.
  3. End of the string should not have any exclamation marks, due to direction: rtl - they will be moved to the left of the string. I think, it is possible to fix this with putting right part of the word in the tag and exclamation mark in the ::after pseudo-element. But I haven't yet made it properly working.

But, with all of these, it looks really cool to me, especially when you dragging the border of the browser, which you can do on the jsfiddle page easily: https://jsfiddle.net/extempl/93ymy3oL/. Or just run the snippet with fixed max-width below.

Gif under the spoiler:

Gif

body {
  max-width: 400px;
}

span::before, span::after {
  display: inline-block;
  max-width: 50%;
  overflow: hidden;
  white-space: pre;
}

span::before {
  content: attr(data-content-start);
  text-overflow: ellipsis;
}

span::after {
  content: attr(data-content-end);
  text-overflow: '';
  direction: rtl;
}
<span data-content-start="Look deep into nature, and then you " 
      data-content-end=  "will understand everything better"></span>

<br>
<span data-content-start="https://www.google.com.ua/images/branding/g" 
      data-content-end=  "ooglelogo/2x/googlelogo_color_272x92dp.png"></span>

Solution 3:

So my colleague came up with a solution that uses no extra dom elements. We check to see if the div overflows and add a data attribute of the last n characters. The rest is done in css.

Here is some HTML:

<div class="box">
    <div class="ellipsis" data-tail="some">This is my text it is awesome</div>
</div>
<div class="box">
    <div class="ellipsis">This is my text</div>
</div>

And the css:

.box {
    width: 200px;
}

.ellipsis:before {
    float: right;
    content: attr(data-tail);
}

.ellipsis {
    white-space: nowrap;
    text-overflow: ellipsis;
    overflow: hidden;
}

Here is the obligatory jsfiddle for this: http://jsfiddle.net/r96vB/1/

Solution 4:

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');