7

我已经查看了与此相关的类似 SO 帖子,但没有一个与我正在寻找的内容完全相关。假设我有一些文本,我将其放入一个 div 中,并为该 div 添加一些任意(甚至可能是动态的)宽度。有没有什么方法可以让我以编程方式捕获和操作 div 中的单行文本(例如,捕获每一行文本,然后将其包装在其自己的 span 标签中,或者类似的东西使我能够操作单行)?

我已经能够使用等宽字体完成此操作,并且基本上首先每行创建一个跨度并为每个跨度分配相同数量的字符(使用一些额外的代码,所以单词当然不会被截断),但我会喜欢能够使用非等宽字体执行此操作,这当然会导致问题,因为非等宽字体的字符水平间距会有所不同。

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

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

  1. 换行时是否会自动插入我可以访问的换行符?

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

  3. 是否有另一种方法可以保留非等宽文本自然流动的外观,同时能够逐行访问文本?正如我上面所说,我已经用等宽文本实现了这一点,但我的方法依赖于等宽文本的统一水平间距。

4

4 回答 4

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 毫秒和 17 MB,而我的解决方案需要 136 毫秒和 14 MB。

一点建议,尽量限制你的 DOM 操作(我在小提琴中标记了它们)。它们会减慢您的代码速度,因为浏览器需要重新渲染您的页面。我只做2,而你做3 + 2x number of words + 1x number of lines。这可能是解释速度差异的原因。而且文本越长,差异就会越大。

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

于 2013-04-25T20:26:46.763 回答
2

我认为发布我为自己的问题提出的解决方案是一个好主意,因为它看起来很优雅。我不必将每个单词都包含在一个跨度中——我只是使用了一个可见性:隐藏测试跨度来查看该行中的下一个单词是否会导致该行在实际添加下一个之前超出容器的宽度字行。当我把每一行放在一起时,我将它们添加到一个数组中。然后我只是通过遍历数组将每一行附加到容器中。它住在这里(至少有一段时间)。

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

}());
于 2013-04-26T00:31:39.353 回答
0

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

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

于 2013-04-25T19:39:01.927 回答
0

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

于 2013-04-25T19:41:38.910 回答