0

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.

3
  • You could do this in a pretty crude way: find the widest character in your font (I'd guess m or w) and see how many fit. Then use JS to check content length, and if it's too long, replace the last three characters with periods. It won't always be exactly to the end of the line though. Commented Oct 27, 2013 at 18:56
  • How about you render all of the text as usual, then use an absolutely positioned div with a matching background, with an ellipsis and cover up the bottom right hand side of your text container. Commented Oct 27, 2013 at 18:59
  • @porfuse I thought about that one, but the occassional half letter might be really annoying.. Commented Oct 27, 2013 at 19:12

3 Answers 3

3

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/

Sign up to request clarification or add additional context in comments.

Comments

1

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

3 Comments

correct approach, but IMHO the code could be improved significantly. It performs many actions over and over that could be factored outside the loop, and mixes up .html() and .text().
@Alnitak ye I never said it was elegant but in the grand scheme of things I don't think it's too intensive? .text() doesn't convert &hellip; - I supposed you could risk using the actual character or failing that 3 dots? but I never had any performance issues.
Personally I would do the split just once outside the loop, and then just .pop each element. As it stands you're re-reading the DOM to get the current text in each iteration, when you should be using JS variables.
1

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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.