如何捕获div中的一行文本

时间:2013-04-25 19:31:17

标签: javascript jquery html css

我已经查看了与此相关的类似SO帖子,但没有一个涉及到我正在寻找的内容。假设我有一些文本,我把它扔到div中,并为该div添加一些任意(甚至可能是动态的)宽度。有没有什么办法可以在div programmaticaly中捕获和操作单独的文本行(例如,捕获然后将自己的span标记中的每一行文本换起来或类似的东西,这将使我能够操纵单独的行)?

我已经能够使用等宽字体完成此操作,并且基本上首先每行创建一个跨度,并且每个跨度只分配相同数量的字符(使用一些额外的代码,因此单词不会被切断)但我希望能够使用非等宽字体来做到这一点,这当然会引起问题,因为非monopaced字体的字符水平间距会有所不同。

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 commodo 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');

这个输出几乎是你所期望的。它是live here。我想弄清楚的是:

  1. 换行时是否可以自动插入换行符?

  2. DOM中是否有任何其他机制或属性可以用来操纵div中的一行?

  3. 我是否还有其他方法可以保留非等宽文本自然流的外观,同时能够逐行访问文本?正如我上面所说,我用等宽文本完成了这个,但我的方法取决于等宽文本的均匀水平间距。

4 个答案:

答案 0 :(得分:6)

其他答案的建议让我很好奇,所以我把它们放到了测试中,这就是我想出来的:

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));    
}

我添加了很多评论,但随时可以询问是否有不明确的内容。

要查看代码(包括一些奇特的颜色;-)),请查看我的小提琴:http://jsfiddle.net/yZnp8/1/

修改
我把@orb的代码放在我的解决方案旁边:http://jsfiddle.net/yZnp8/5/

与Chrome Inspector的快速比较表明,存在巨大的性能差异。 @orbs解决方案需要754毫秒和17MB,而我的解决方案需要136毫秒和14MB。

一些建议,尝试限制你的DOM操作(我在小提琴中标记它们)。它们会减慢您的代码速度,因为浏览器需要重新呈现您的页面。我只做23 + 2x number of words + 1x number of lines。这可能解释了速度的巨大差异。文本越长,差异就越大。

不试图打破@orb解决方案,只是试图提供帮助并解释差异......

答案 1 :(得分:2)

我认为发布我为自己的问题提出的解决方案是一个很好的想法,因为它看起来非常优雅。我没有必要在一个范围内包装每个单词 - 我只是使用了一个可见性:隐藏的测试范围,看看该行中的下一个单词是否会导致该行在实际添加下一个之前超过容器的宽度说到线。当我把每一行放在一起时,我将它们添加到一个数组中。然后我通过迭代数组将每一行附加到容器。它是实时here(至少有一段时间)。

/*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 commodo 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());
        }
    }

}());

答案 2 :(得分:0)

不确定我完全理解你的问题,但是......你能够得到div的高度吗?除以(CSS定义的)行高,你应该有文本行数。

然后,您可以通过创建包含原始文本的部分的不可见div来检查任何给定字符/单词的位置,并执行上述技巧来查找其行。这有帮助吗?

答案 3 :(得分:0)

Bob Monteverde提供了一段非常好的代码来计算字符串宽度here。您可以使用它并开始将字符串宽度与div的实际宽度进行比较,以找到单行。