views:

125

answers:

4

I'm trying to create a universal function that I can call from multiple places to truncate long text recursively to fit a predefined pixel width - using jquery.

Here is the code...

function constrain(text, original, ideal_width){

 var ideal = parseInt(ideal_width);

 $('span.temp_item').remove();
 var temp_item = ('<span class="temp_item" style="display:none">'+ text +'</span>');
 var item_length = text.length; 
 $(temp_item).appendTo('body');
 var item_width = $('span.temp_item').width();

 if (item_width > ideal) {
  var smaller_text = text.substr(0, (item_length-1));
  return constrain(smaller_text, original);
 } else if (item_length != original) {
  return (text + '&hellip;');
 } else if (item_length == original) {
  return text;
 }
}

If I run the function like this:

    $('.service_link span:odd').each(function(){
 var item_text = $(this).text();
 var original_length = item_text.length;
 var constrained = constrain(item_text, original_length,'175');
 $(this).html(constrained);
});

The text doesn't truncate. I also tried the 175 without the quotes.

If I define var ideal = 175; inside the function, then it works. Why is passing 175 to the function not working? I did a parseInt on it in case it was a string.

Also - this truncate code run a bit slow on older machines - any tips for speeding it up?

Thanks!

A: 

What happens when the visitor to your site presses "ctl +" ? It's my (probably out of date) belief that you're supposed to use "em" sizes for font containers, so they scale.

ryansstack
Well it still works in that case since it's calculating width by pixel instead of length of characters.
novon
A: 

TOTAL WE WRITE

So I decided that your iteration over the lorum ipsum text in 5 spans, taking 16 secs was far too long, so thought how to speed this up. and I have it down to 0.4 seconds.

function constrain(text, original, ideal_width, counter){

    var ideal = parseInt(ideal_width);

    $('span.temp_item').remove();
    var temp_item = ('<span class="temp_item" style="display:none">'+ text +'</span>');
    var item_length = text.length; 
    $(temp_item).appendTo('body');
    var item_width = $('span.temp_item').width();

    if(counter == 0) {
 //work out some ranges
 var temp_item = ('<span class="temp_item_i" style="display:none">i</span>');
        $(temp_item).appendTo('body');
        var i_width = $('span.temp_item_i').width();

 var max_i = Math.round((ideal_width / i_width) + 1);

 var temp_item = ('<span class="temp_item_m" style="display:none">M</span>');
        $(temp_item).appendTo('body');
        var m_width = $('span.temp_item_m').width();

 var max_m = Math.round((ideal_width / m_width) + 1);

 text = text.substr(0, (max_i - max_m));

        var item_length = text.length; 
    }
    counter++;


while(item_width > ideal) {   
            var smaller_text = text.substr(0, (item_length-1));
            return constrain(smaller_text, original, ideal_width, counter);
    } 

    if (item_length != original) {

            return (text + '&hellip;');
    } else if (item_length == original) {
            return text;
    }
}

$(document).ready(function() {
var d = new Date();
var s = d.getTime();

$('.service_link').each(function(){
        var item_text = $(this).text();
        var original_length = item_text.length;
        var constrained = constrain(item_text, original_length, 175, 0);
        $(this).html(constrained);
});

var e = d.getTime()

alert('Time Taken: ' + ((e - s)/1000));
});

Basically on the first run, it works out how many lowercase i's and how many uppercase Ms fit in the space, and then restricts the text length to that, this reduces the number of iterations massively.

Hope this helps.

Phil Carter
'ideal' is a hard pixel width, not number of characters, otherwise it would break as font size increases or if characters with wide pixel counts are used.
novon
I have amended to take this into consideration, this problem really bugged me till i got it decent. though it's still not perfect.
Phil Carter
A: 

Ah... found the bug - forgot to pass the recursive part the ideal width:

return constrain(smaller_text, original, ideal);
novon
A: 

Great stuff here. I used the function by Phil Carter. I just wanted the new string with the &hellip to be truncated at the same width as the rest.

I just quickly added another temp-width lookup and recursive call. Could use some cleanup but it works. here's the new while:

while(item_width > ideal) {                     
            var smaller_text = text.substr(0, (item_length-1));
            return constrain(smaller_text, original, ideal_width, counter);
    } 

    if (item_length != original) {
      new_text=text+'&hellip;';
      $('span.temp_item').remove();
      var temp_item = ('<span class="temp_item" style="display:none">'+ new_text +'</span>');
      $(temp_item).appendTo('body');
      var item_width_new = $('span.temp_item').width();
     if(item_width_new>ideal){
            var smaller_text = text.substr(0, (item_length-1));
            return constrain(smaller_text, original, ideal_width, counter);   
     }
     else {
      return new_text;
     }
    } else if (item_length == original) {
            return text;
    }
}
DaMayan