最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to capture one line of text in a div - Stack Overflow

programmeradmin1浏览0评论

I've looked around at similar SO posts related to this, but none deal with exactly what I am looking for. Say I have some text, I throw it in a div, and add some arbitrary (possibly even dynamic) width to that div. Is there any way that I can then capture and manipulate individual lines of text in the div programmaticaly (say, for example, capture and then wrap every line of text in its own span tag or something like this that will enable me to manipulate individual lines)?

I have been able to acplish this using a monospace font and basically first creating one span per line and just assigning the same number of characters per span (with a little extra code so words don't get cut off of course), but I would like to be able to do this with non-monospaced fonts, which will cause an issue of course because horizontal spacing of characters varies for non-monopsaced fonts.

var str = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea modo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
    container = $('<div>');
    container.width('100px').html(str).appendTo('body');

The output of this is pretty much what you expect. It is live here. What I seek to figure out:

  1. Are newline characters that I can access automatically inserted when the line breaks?

  2. Are there any other mechanisms or properties in the DOM I can hack into to manipulate one line in a div?

  3. Is there another approach I haven't though of to be able to retain the appearance of natural flow of non-monospaced text while being able to access text line-by-line? As I said above, I have acplished this with monospaced text, but my approach was dependent on the uniform horizontal spacing of monospaced text.

I've looked around at similar SO posts related to this, but none deal with exactly what I am looking for. Say I have some text, I throw it in a div, and add some arbitrary (possibly even dynamic) width to that div. Is there any way that I can then capture and manipulate individual lines of text in the div programmaticaly (say, for example, capture and then wrap every line of text in its own span tag or something like this that will enable me to manipulate individual lines)?

I have been able to acplish this using a monospace font and basically first creating one span per line and just assigning the same number of characters per span (with a little extra code so words don't get cut off of course), but I would like to be able to do this with non-monospaced fonts, which will cause an issue of course because horizontal spacing of characters varies for non-monopsaced fonts.

var str = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea modo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
    container = $('<div>');
    container.width('100px').html(str).appendTo('body');

The output of this is pretty much what you expect. It is live here. What I seek to figure out:

  1. Are newline characters that I can access automatically inserted when the line breaks?

  2. Are there any other mechanisms or properties in the DOM I can hack into to manipulate one line in a div?

  3. Is there another approach I haven't though of to be able to retain the appearance of natural flow of non-monospaced text while being able to access text line-by-line? As I said above, I have acplished this with monospaced text, but my approach was dependent on the uniform horizontal spacing of monospaced text.

Share Improve this question edited Apr 25, 2013 at 21:32 orb asked Apr 25, 2013 at 19:31 orborb 1,2841 gold badge10 silver badges17 bronze badges 3
  • 1 best solution is to probably wrap every word in a span, then check the vertical offset of the spans in order. when the offset of word "x+1" is higher than word "x", you've reached a new line. – Marc B Commented Apr 25, 2013 at 19:35
  • Did you ever see this: stackoverflow./questions/4147080/… (example: jsbin./avuku/15) – Joe Commented Apr 25, 2013 at 19:36
  • Hey I just want everyone know that I think I actually came up with a really solid answer to this that is posted below. It is definitely worth checking out if you want to capture single lines of text from a div. – orb Commented Apr 26, 2013 at 2:39
Add a ment  | 

4 Answers 4

Reset to default 6

The suggestions from the other answers made me curious so i put them to the test, and this is what i came up with:

function wrapLines($container) {
    // get the text from the conatiner
    var text = $container.text();

    // split the text into words
    var words = text.split(' ');

   // wrap each word in a span and add it to a tmp
   var tmp = '';
   tmp += '<span>' + words.join('</span><span>') + '</span> ';

   // remove the text from the container, and replace it with the wrapped words
   $container.html($(tmp));

    // prepare the offset variable and tmp
    var tmp = '';
    var top = null;
    $container.find('span').each(function(index, word) {
        $word = $(word);
        // if this is the first iteration
        if (top == null) {
            // set the top
            top = $word.position().top;
            // open the first line
            tmp = '<span class="line">';
        }

        // if this is a new line (top is bigger then the previous word)
        if (top < $word.position().top) {
            // close the previous line and start a new one
            tmp += '</span><span class="line">';
            // change the top
            top = $word.position().top;            
        }

        // add the content of the word node + a space
        tmp += $word.text() + ' ';
    });
    // close the last line
    tmp += '</span>';

    // remove the content of the conatiner, and replace it with the wrapped lines
    $container.html($(tmp));    
}

I added plenty of ments, but feel free to ask if something isn't clear.

To see the code in action (including some fancy colors ;-) ), have a look at my fiddle: http://jsfiddle/yZnp8/1/

edit:
I put the code from @orb next to my solution here: http://jsfiddle/yZnp8/5/.

A quick parison with Chrome Inspector shows that there is a big performance diiference. @orbs solution takes 754ms and 17MB while my solution takes 136ms and 14MB.

A little word of advice, try to limit your DOM operations (I marked them in the fiddle). They slow your code down, as the browser needs to render your page all over again. I do only 2, while you do 3 + 2x number of words + 1x number of lines. This is probably what explains the big difference in speed. And the longer the text, the bigger the difference will bee.

Not trying to break @orb solution down, just trying to be helpful and explain the differences...

I thought it would be a good ideea to post the solution I came up with for my own question, because it seems pretty elegant. I didn't have to wrap every word in a span—I just used a visibility:hidden test span to see if the next word in the line was going to cause the line to run past the width of the container before actually adding the next word to the line. As I put together each line I added them to an array. I then just appended each line to the container by iterating over the array. It is live here (at least for a while).

/*global console, $, OO*/
/*jslint browser: true*/
(function (undefined) {
    "use strict";

    $(window).on('load', function (event) {
        var str = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea modo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
            $container = $('<div>').width('200px').text(str).appendTo('body');

        wrapLines($container);
    });

    function wrapLines($container) {
        var text = $container.text(),
            words = text.split(' '),
            lines = [],
            line = $('<span>'),
            tmp = $('<span>').css('visibility', 'hidden').appendTo('body');

        $container.text("");

        $.each(words, function (index, word) {
            if (tmp.text(line.text() + " " + word).width() < $container.width()) {
                line.text(line.text() + " " + word);
            } else {
                lines.push(line);
                line.remove();
                line = $('<span>');
                tmp.text("");
            }
        });

        tmp.remove();

        for (var kittens = 0 ; kittens < lines.length; kittens++) {
            lines[kittens].appendTo($container);
            console.log(lines[kittens].width());
        }
    }

}());

Not sure I pletely understand your question, but... You will be able to get the div's height right? Divide that by a (CSS-defined) line-height and you should have the number of lines of text.

You could then check the position of any given character / word by making an invisible div containing part of the original text and performing the abovementioned trick to find its line. Does that help?

Bob Monteverde provided a very nice piece of code to calculate a Strings width here. You could use that and start paring string widths with the actual width of your div to find single lines.

发布评论

评论列表(0)

  1. 暂无评论