14

Is it possible to somehow measure the length of the last line of text in a paragraph that has multiple breaks / returns?

          Morbi leo risus, porta ac consectetur ac, vestibulum at eros.
          Donec id elit non mi porta gravida at eget metus. Nulla vitae
          elit libero, a pharetra augue. Nullam id dolor id nibh
          ultricies vehicula ut id elit. Vivamus sagittis lacus vel
[here] ->|augue laoreet rutrum faucibus dolor auctor.|<- [to here]

Note: There are no manual breaks in the text. It is a single line of text wrapped inside, let's say, a <p></p> tag.

Andrew Tibbetts
  • 2,874
  • 3
  • 23
  • 28
  • Maybe this will help: http://stackoverflow.com/questions/118241/calculate-text-width-with-javascript – Peter Mar 07 '14 at 21:57
  • There's the technique of cloning a string into a hidden inline element to be measured (like in autosizing input elements to their values). So, I guess the sticking point is that it's the last line of a paragraph. – Andrew Tibbetts Mar 07 '14 at 22:13

3 Answers3

21

It should be sufficient to append a zero-width element at the very end of the string and measure its left offset.

HTML

<p id="text">…text…</p>

JS

// NOTE: This assumes LTR text!
// Using JQuery for simpler code

var $el = $("#text");
var originalText = $el.html();

$el.html(originalText + "<span id='cursor'></span>");
alert($("#cursor").offset().left + "px");
$el.html(originalText);

JSFiddle: http://jsfiddle.net/Ca4fF/1/

Nelson Menezes
  • 2,036
  • 14
  • 15
3

Try this http://jsfiddle.net/Qb9WX/3/

HTML

<div id="demo">
    Morbi leo risus, porta ac consectetur ac, vestibulum at eros.
    Donec id elit non mi porta gravida at eget metus. Nulla vitae
    elit libero, a pharetra augue. Nullam id dolor id nibh
    ultricies vehicula ut id elit. Vivamus sagittis lacus vel
    augue laoreet rutrum faucibus dolor auctor.
</div>

JS

$(document).ready(function() {
    var content = $('#demo').html();
    content = content.replace(/(\w|\s)/g, '<span>$1</span>');
    $('#demo').html(content);

    // Check each <span> for its offsetTop
    var highest_top = 0;
    var tmp_top = 0;
    $('#demo span').each(function() {
        tmp_top = $(this)[0].offsetTop;
        if (tmp_top > highest_top) {
            highest_top = tmp_top;
        }
    });

    // Collect total width
    var total_width = 0;
    $('#demo span').each(function() {
        check_top = $(this)[0].offsetTop;
        if (check_top == highest_top) {
            total_width += $(this).width();
        }
    });

    console.log(total_width);
});

You can tweak it to your own needs.

For me it gives 88px in the fiddle:

enter image description here

You can buffer back the original (span-less) string into the element too after doing the calculations. This way you don't need to keep the cluttered elements.

A final note; if you use foreign characters (like the German ß or Russian/Japanese/etc.) the regex might fail to match on \w. You need to expand your knowledge on character-set matching then. But that goes beyond the scope of this question.

Small (delayed) update

You might want to change:

/(\w|\s)/g

to something like:

/([^\n\t])/g

This way you will match anything except tabs and newlines. I noticed the pixel count might be a bit off if you match only letters and spaces. It might miss important comma's and other read-signs.

0

Edit: this was written before the clarification that there are no line breaks in the paragraph. Sorry, not applicable.

If you trim whitespace at beginning and ending of the string (ie the paragraph), and then split around newlines and take the last element of the result, you should be close to getting what you want.

<div class='input'>
Morbi leo risus, porta ac consectetur ac, vestibulum at eros.
Donec id elit non mi porta gravida at eget metus. Nulla vitae
elit libero, a pharetra augue. Nullam id dolor id nibh
ultricies vehicula ut id elit. Vivamus sagittis lacus vel
augue laoreet rutrum faucibus dolor auctor.
</div>
<div class='result'>
</div>

and

$('div.result').html( "" )
$('div.input').each( function(){
    var lines = $(this).html(
       ).replace(/^\s+/, ''
       ).replace(/\s+$/,''
       ).split("\n")
    $('div.result').append("<p><kbd>" + 
       lines[lines.length-1].length + 
       "</kbd>: <i>"+lines[lines.length-1]+"</p>" 
    )       
})

http://jsfiddle.net/xbFAx/

erik258
  • 14,701
  • 2
  • 25
  • 31
  • I wasn't clear—there are no manual breaks in the text. It is a single line of text wrapped inside, let's say, a `

    ` tag.
    – Andrew Tibbetts Mar 07 '14 at 22:16
  • ooh, tricky. That's a lot harder. And likely dependent on display characteristics. Sorry, can't think of a way to do that. – erik258 Mar 07 '14 at 22:18