Ziarno
Ziarno

Reputation: 7562

Word wrap: ellipsis without css

I'm developing an app for a TV with an old Gecko engine (1.9 I think). I have a container 250x40 and I'd like to fit 2 lines of text in it, and if it's too long then an ellipsis should be shown (just like in the CSS3 property text-overflow: ellipsis).

However: - I cannot use CSS3, - I tried using some jQuery plugins, but they just work too slow - you can accually see the text being shrunk down until it fits in the container.

I tried counting letters, but it doesn't work, because they are all different widths. I tried mapping each letter to its width, and counting the widht of the whole text, but the fact that it's 2 lines screws it all up - I don't know at which point the text will go to the next line.

Any help appreciated.

Upvotes: 0

Views: 905

Answers (3)

Alnitak
Alnitak

Reputation: 339796

Slightly based on @Emissary's answer, here's a reasonably performing pair of jQuery plugins that'll handle adding ellipsis on elements that might hold more than one row of text:

(function($) {

    $.fn.reflow = function() {
        return this.each(function() {

            var $this = $(this);
            var $parent = $this.parent();
            var text = $this.data('reflow');
            $this.text(text);               // try full text again

            var words = text.split(' ');
            while ($this.height() > $parent.height() ||
                   $this.width() > $parent.width()) {
                words.pop();
                $this.html(words.join(' ') + '…');
            }
        });
    }

    $.fn.overflow = function() {
        return this.each(function() {
            var $this = $(this);
            var text = $this.text();
            $this.data('reflow', text);
        }).reflow();
    }

})(jQuery);

The latter one registers elements for reflowing, and the first one actually does the work. The split is there to allow window resizing to automatically reformat the (original) contents of the element, e.g.:

$('.overflow').overflow();
$(window).on('resize', function() {
    $('.overflow').reflow();
});

For higher performance, if it matters, you might consider replacing the .pop sequence with something that uses a O(log n) binary partitioning algorithm to find the optimal cut point instead of just removing one word at a time.

See http://jsfiddle.net/alnitak/vyth5/

Upvotes: 3

Henry Florence
Henry Florence

Reputation: 2866

This chap here has a solution that uses javascript and no JQuery: http://blog.mastykarz.nl/measuring-the-length-of-a-string-in-pixels-using-javascript/

Done in jsfiddle: http://jsfiddle.net/ZfDYG/

Edit - just read the bit about 2 lines of text: http://jsfiddle.net/ZfDYG/8/

code (for completeness):

String.prototype.visualLength = function() {
  var ruler = $("ruler");
  ruler.innerHTML = this;
  return ruler.offsetWidth;
}
function $(id) {
  return document.getElementById(id);
}
var s = "Some text that is quite long and probably too long to fit in the box";
var len = s.visualLength();

String.prototype.trimToPx = function(length,postfix) {
  postfix = postfix || '';
  var tmp = this;
  var trimmed = this;
  if (tmp.visualLength() > length) {
    trimmed += postfix;
    while (trimmed.visualLength() > length) {
      tmp = tmp.substring(0, tmp.length-1);
      trimmed = tmp + postfix;
    }
  }

  return trimmed;
}
String.prototype.wrapToLength = function(complete) {
  if(complete[this.length] == ' ' || complete[this.length - 1] == ' ') return;

  var wrapped = this;
  var found = false;
  for(var i = this.length-1; i > 0 && !found; i--) {
    if(this[i] == ' ') { 
      wrapped = this.substring(0, i); 
      found = true; 
    }
  }
  return wrapped;
}
var firstLine = s.trimToPx(240).wrapToLength(s);
var secondLine = s.substring(firstLine.length, s.length);
$('output').innerHTML= firstLine+' '+secondLine.trimToPx(240,'...');

Html:

<span id="ruler"></span>
<div id="output"></div>

css:

#ruler { visibility: hidden; white-space: nowrap; }
#output { 
  width: 240px;
  height: 50px;
  border: 1px solid red;
}

If that is still too slow on your box, I guess it should be possible to speed up the while loop by starting with bigger additions to oscillate towards the final length (kinda like a spring) rather than increment slowly up to it.

Upvotes: 1

Emissary
Emissary

Reputation: 10148

It's been a while since I bothered with supporting older browsers but this is how I always did it. Should point out that it trims words rather than characters, I always thought half a word looked daft - if you care about typography...

html:

<div id="el">
    <span class="overflow">Lorem ipsum dolor sit amet</span>
</div>

css:

#el { 
    width: 250px;
    height: 40px;
    overflow: hidden;
}
.overflow {
    white-space: nowrap;
}

js / jQuery:

var el = $('#el'),
    ov = $('#el .overflow'),
    w = el.text().split(' ');
while(ov.width() > el.width()){ 
    w.pop();     
    ov.html( w.join(' ') + '&hellip;' );
}

jsFiddle

Upvotes: 1

Related Questions