Javascript 寻找换行符

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3738490/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 05:57:04  来源:igfitidea点击:

Finding line-wraps

javascriptjqueryhtmldomtext

提问by Inaimathi

Supposing I have some random block of text in a single line. Like so

假设我在一行中有一些随机的文本块。像这样

Lorem ipsum dolor sit amet, consectetur adipiscing elit.

Lorem ipsum dolor sit amet, consectetur adipiscing elit.

But for whatever reason (width settings on the containing element, use of text-zoom etc.), on the viewer's screen it displays as two or more lines.

但无论出于何种原因(包含元素的宽度设置、文本缩放的使用等),它在查看器的屏幕上显示为两行或更多行。

Lorem ipsum dolor sit amet,

Lorem ipsum dolor sit amet,

consectetur adipiscing elit.

consectetur adipiscing elit.

or

或者

Lorem ipsum dolor sit

Lorem ipsum dolor sit

amet, consectetur

amet, consectetur

adipiscing elit.

adipiscing elit.

Is there any way to find out via javascript where those line-wraps happen?

有没有办法通过javascript找出这些换行发生的地方?

$('p').text()and $('p').html()return Lorem ipsum dolor sit amet, consectetur adipiscing elit.regardless of how the text is displayed.

$('p').text()$('p').html()返回Lorem ipsum dolor sit amet, consectetur adipiscing elit.不管是如何显示的文本。

采纳答案by Inaimathi

Here's what I ended up using (feel free to critique and copy for your own nefarious purposes).

这是我最终使用的内容(为了您自己的邪恶目的,请随意批评和复制)。

First off, when the edit comes in from the user, it's broken up with $(editableElement).lineText(userInput).

首先,当编辑来自用户时,它被分解为$(editableElement).lineText(userInput).

jQuery.fn.lineText = function (userInput) {
   var a = userInput.replace(/\n/g, " \n<br/> ").split(" ");
   $.each(a, function(i, val) { 
      if(!val.match(/\n/) && val!="") a[i] = '<span class="word-measure">' + val + '</span>';
   });
   $(this).html(a.join(" "));
};

The newline replacement happens because the editing textbox is populated with $(editableElement).text(), which ignores <br/>tags, but they will still change the height of the following line in the display for typesetting purposes. This was not part of the initial objective, just fairly low-hanging fruit.

发生换行替换是因为编辑文本框填充了$(editableElement).text(),这会忽略<br/>标签,但出于排版目的,它们仍会更改显示中下一行的高度。这不是最初目标的一部分,只是悬而未决的成果。

When I need to pull out formatted text, I call $(editableElement).getLines(), where

当我需要拉出格式化文本时,我调用$(editableElement).getLines(), where

jQuery.fn.getLines = function (){
   var count = $(this).children(".word-measure").length;
   var lineAcc = [$(this).children(".word-measure:eq(0)").text()];
   var textAcc = [];
   for(var i=1; i<count; i++){
      var prevY = $(this).children(".word-measure:eq("+(i-1)+")").offset().top;
      if($(this).children(".word-measure:eq("+i+")").offset().top==prevY){
         lineAcc.push($(this).children(".word-measure:eq("+i+")").text());
   } else {
     textAcc.push({text: lineAcc.join(" "), top: prevY});
     lineAcc = [$(this).children(".word-measure:eq("+i+")").text()];
   }
   }
   textAcc.push({text: lineAcc.join(" "), top: $(this).children(".word-measure:last").offset().top});
   return textAcc;
};

The end result is a list of hashes, each one containing the content and vertical offset of a single line of text.

最终结果是一个哈希列表,每个哈希都包含一行文本的内容和垂直偏移。

[{"text":"Some dummy set to","top":363},
 {"text":"demonstrate...","top":382},
 {"text":"The output of this","top":420},
 {"text":"wrap-detector.","top":439}]

If I just want unformatted text, $(editableElement).text()still returns

如果我只想要无格式文本,$(editableElement).text()仍然返回

"Some dummy set to demonstrate... The output of this wrap-detector."

回答by Yi Jiang

Well, if you want something that's ridiculously simple and probably too useless for you (it'll need major modification if you have any sort of HTML inside the paragraph), then have a look at this:

好吧,如果你想要一些非常简单并且可能对你来说太无用的东西(如果你在段落中有任何类型的 HTML,它需要进行重大修改),然后看看这个:

var para = $('p');

para.each(function(){
    var current = $(this);
    var text = current.text();
    var words = text.split(' ');

    current.text(words[0]);
    var height = current.height();

    for(var i = 1; i < words.length; i++){
        current.text(current.text() + ' ' + words[i]);

        if(current.height() > height){
            height = current.height();
            // (i-1) is the index of the word before the text wraps
            console.log(words[i-1]);
        }
    }
});

It's so ridiculously simple it might just work. What this does is to break up the text by spaces, then append the words back word by word, watching for any increase in the height of the element, which would indicate a line wrap.

它是如此简单可笑,它可能只是工作。这样做是将文本按空格分开,然后逐字追加单词,观察元素高度的任何增加,这将表明换行。

Have a look at it here: http://www.jsfiddle.net/xRPYN/2/

在这里看看:http: //www.jsfiddle.net/xRPYN/2/

回答by balupton

For a use case like pdf generation.

对于像 pdf 生成这样的用例。

You can limit to characters per line, if a split occurs middle word, adjust appropriately.

您可以限制每行字符,如果中间单词出现拆分,请适当调整。

To gain a more accurate characters per line you can use monospaced fonts then determine the width per character for each font allowed. Then divide the character width by the size of the allowed text line width, and you'll have the allowed characters per line for that font.

要获得每行更准确的字符,您可以使用等宽字体,然后确定允许的每种字体的每个字符的宽度。然后将字符宽度除以允许的文本行宽的大小,您将拥有该字体的每行允许的字符。

You could use non monospaced fonts, but then you'll have to measure each letter's width - ugh. A way you can automate the width guessing is having a span that has no margin or padding, add in each character for each font (and size) then measure the width of the span and use that.

您可以使用非等宽字体,但是您必须测量每个字母的宽度 - 呃。您可以自动进行宽度猜测的一种方法是使用没有边距或填充的跨度,为每种字体(和大小)添加每个字符,然后测量跨度的宽度并使用它。

I've done up the code:

我已经完成了代码:

/**
 * jQuery getFontSizeCharObject
 * @version 1.0.0
 * @date September 18, 2010
 * @since 1.0.0, September 18, 2010
 * @package jquery-sparkle {@link http://www.balupton/projects/jquery-sparkle}
 * @author Benjamin "balupton" Lupton {@link http://www.balupton.com}
 * @copyright (c) 2010 Benjamin Arthur Lupton {@link http://www.balupton.com}
 * @license Attribution-ShareAlike 2.5 Generic {@link http://creativecommons.org/licenses/by-sa/2.5/
 */
$.getFontSizeCharObject = function(fonts,sizes,chars){
    var fonts = fonts||['Arial','Times'],
        sizes = sizes||['12px','14px'],
        chars = chars||['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','y','x','z',
                        'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','Y','X','Z',
                        '0','1','2','3','4','5','6','7','8','9','-','=',
                        '!','@','#','$','%','^','&','*','(',')','_','+',
                        '[',']','{','}','\','|',
                        ';',"'",':','"',
                        ',','.','/','<','>','?',' '],
        font_size_char = {},
        $body = $('body'),
        $span = $('<span style="padding:0;margin:0;letter-spacing:0:word-spacing:0"/>').appendTo($body);

    $.each(fonts, function(i,font){
        $span.css('font-family', font);
        font_size_char[font] = font_size_char[font]||{};
        $.each(sizes, function(i,size){
            $span.css('font-size',size);
            font_size_char[font][size] = font_size_char[font][size]||{};
            $.each(chars,function(i,char){
                if ( char === ' ' ) {
                    $span.html('&nbsp;');
                }
                else {
                    $span.text(char);
                }
                var width = $span.width()||0;
                font_size_char[font][size][char] = width;
            });
        });
    });

    $span.remove();

    return font_size_char;
};

/**
 * jQuery adjustedText Element Function
 * @version 1.0.0
 * @date September 18, 2010
 * @since 1.0.0, September 18, 2010
 * @package jquery-sparkle {@link http://www.balupton/projects/jquery-sparkle}
 * @author Benjamin "balupton" Lupton {@link http://www.balupton.com}
 * @copyright (c) 2010 Benjamin Arthur Lupton {@link http://www.balupton.com}
 * @license Attribution-ShareAlike 2.5 Generic {@link http://creativecommons.org/licenses/by-sa/2.5/
 */
$.fn.adjustedText = function(text,maxLineWidth){
    var $this = $(this),
        font_size_char = $.getFontSizeCharObject(),
        char_width = font_size_char['Times']['14px'],
        maxLineWidth = parseInt(maxLineWidth,10),
        newlinesAt = [],
        lineWidth = 0,
        lastSpace = null;

    text = text.replace(/\s+/g, ' ');

    $.each(text,function(i,char){
        var width = char_width[char]||0;
        lineWidth += width;
        if ( /^[\-\s]$/.test(char) ) {
            lastSpace = i;
        }
        //console.log(i,char,lineWidth,width);
        if ( lineWidth >= maxLineWidth ) {
            newlinesAt.push(lastSpace||i);
            lineWidth = width;
            lastSpace = null;
        }
    });

    $.each(newlinesAt,function(i,at){
        text = text.substring(0,at+i)+"\n"+text.substring(at+i);
    });

    text = text.replace(/\ ?\n\ ?/g, "\n");

    console.log(text,newlinesAt);

    $this.text(text);

    return $this;
};

$(function(){
    var $body = $('body'),
        $textarea = $('#mytext'),
        $btn = $('#mybtn'),
        $div = $('#mydiv');

    if ( $textarea.length === 0 && $div.length === 0 ) {
        $body.empty();

        $textarea = $('<textarea id="mytext"/>').val('(When spoken repeatedly, often three times in succession: blah blah blah!) Imitative of idle, meaningless talk; used sometimes in a slightly derogatory manner to mock or downplay another\'s words, or to show disinterest in a diatribe, rant, instructions, unsolicited advice, parenting, etc. Also used when recalling and retelling another\'s words, as a substitute for the portions of the speech deemed irrelevant.').appendTo($body);
        $div = $('<div id="mydiv"/>').appendTo($body);
        $btn = $('<button id="mybtn">Update Div</button>').click(function(){
            $div.adjustedText($textarea.val(),'300px');
        }).appendTo($body);

        $div.add($textarea).css({
            'width':'300px',
            'font-family': 'Times',
            'font-size': '14px'
        });
        $div.css({
            'width':'auto',
            'white-space':'pre',
            'text-align':'left'
        });
    }

});

回答by xdamman

The solutions above don't work once you have more complex structure like a link in a paragraph (e.g. you can have <b><i><a href></a>within a <p>).

一旦您拥有更复杂的结构,如段落中的链接(例如,您可以<b><i><a href></a><p>.

So I made a javascript library to detect where lines wrap that works in those cases: http://github.com/xdamman/js-line-wrap-detector

所以我做了一个 javascript 库来检测在这些情况下行的换行位置:http: //github.com/xdamman/js-line-wrap-detector

I hope this helps.

我希望这有帮助。

回答by user3761817

I have a situation where I need to wrap each line in a span. I do this so that I can add a padded highlight effect to a text block. Adding the background to a span tag that wraps the text will only pad the beginning and ending of the text block, each line must be wrapped individually.

我有一种情况,我需要将每一行都包裹在一个跨度中。我这样做是为了向文本块添加填充突出显示效果。将背景添加到环绕文本的 span 标签只会填充文本块的开头和结尾,每行必须单独包装。

This is what I came up with based on the suggestions above:

这是我根据上面的建议提出的:

$.fn.highlghtWrap = function () {
    this.each( function () {
      var current = $( this );
      var text = current.text();
      var words = text.split( ' ' );
      var line = '';
      var lines = [];

      current.text( words[ 0 ] );
      var height = current.height();
      line = words[ 0 ];
      for ( var i = 1; i < words.length; i++ ) {
        current.text( current.text() + ' ' + words[ i ] );

        if ( current.height() > height ) {
          lines.push( line );
          line = words[ i ];
          height = current.height();
        } else {
          line = line + ' ' + words[ i ];
        }
      }
      lines.push( line );
      current.html( '' );
      $.each( lines, function ( v, a ) {
        current.html( current.html() + '<span>' + a +
          ' </span>' );
      } );
    } );
  }

  $( '.home-top_wrapper h2' ).highlghtWrap();
  $( '.home-top_wrapper p' ).highlghtWrap();

回答by TextGeek

A conceptually simple way that also works when there's internal markup and arbitrary fonts and styles, is to make a first pass that simply puts every word into its own element (maybe 'SPAN', or a custom name like 'w').

当存在内部标记和任意字体和样式时,一种概念上简单的方法也适用,即首先将每个单词放入其自己的元素中(可能是“SPAN”,或像“w”这样的自定义名称)。

Then you can iterate using getBoundingClientRect() to find where the 'top' property changes:

然后,您可以使用 getBoundingClientRect() 进行迭代以查找 'top' 属性更改的位置:

function findBreaks() {
    var words = document.getElementsByTagName('w');
    var lastTop = 0;
    for (var i=0; i<words.length; i++) {
        var newTop = words[i].getBoundingClientRect().top;
        if (newTop == lastTop) continue;
        console.log("new line " + words[i].textContent + " at: " + newTop);
        lastTop = newTop;
    }
}

It sounds slow, but unless the documents are really big you won't notice.

这听起来很慢,但除非文档非常大,否则您不会注意到。