web-dev-qa-db-ja.com

行の折り返しを見つける

1行にランダムなテキストブロックがあるとします。そのようです

_Lorem ipsum dolor sit amet, consectetur adipiscing elit._

ただし、なんらかの理由(含まれる要素の幅設定、テキストズームの使用など)のために、ビューアの画面では2行以上として表示されます。

_Lorem ipsum dolor sit amet,_

_consectetur adipiscing elit._

または

_Lorem ipsum dolor sit_

_amet, consectetur_

_adipiscing elit._

これらの行の折り返しが発生する場所をJavaScriptで確認する方法はありますか?

$('p').text()および$('p').html()は、テキストの表示方法に関係なく_Lorem ipsum dolor sit amet, consectetur adipiscing elit._を返します。

40
Inaimathi

これが私が最終的に使用したものです(ご自身の悪質な目的のために批評してコピーしてください)。

まず、編集がユーザーから行われると、$(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(" "));
};
_

改行の置換は、編集テキストボックスに$(editableElement).text()が入力され、_<br/>_タグを無視するために発生しますが、タイプセットの目的で、次の行の高さが変更されます。これは当初の目的の一部ではなく、かなり低い成果でした。

フォーマットされたテキストを取り出す必要があるときは、$(editableElement).getLines()を呼び出します。

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

最終結果はハッシュのリストで、各ハッシュには1行のテキストのコンテンツと垂直オフセットが含まれます。

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

未フォーマットのテキストが必要な場合でも、$(editableElement).text()は引き続き返されます

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

まあ、とんでもないほど単純でおそらく役に立たないもの(段落内に何らかの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]);
        }
    }
});

それはとんでもないほど単純で、うまくいくかもしれません。これは、テキストをスペースで分割し、単語を単語ごとに追加し、要素の高さが増加するのを監視します。これは、行の折り返しを示します。

ここでそれを見てください: http://www.jsfiddle.net/xRPYN/2/

27
Yi Jiang

PDF生成などのユースケース。

1行あたりの文字数を制限できます。Wordの途中で分割された場合は、適切に調整してください。

1行あたりの文字数をより正確にするには、等幅フォントを使用して、許可される各フォントの1文字あたりの幅を決定します。次に、文字の幅を許容されるテキスト行の幅のサイズで割ると、そのフォントの1行あたりの許容文字が得られます。

等幅でないフォントを使用することもできますが、その場合は各文字の幅を測定する必要があります-うーん。幅の推測を自動化する方法として、マージンやパディングのないスパンを用意し、各フォント(およびサイズ)の各文字を追加してから、スパンの幅を測定してそれを使用します。

私はコードを完成させました:

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

});
13
balupton

上記の解決策は、段落内のリンクのようなより複雑な構造になると機能しません(たとえば、<b><i><a href></a> 間に <p>)。

だから私はそれらの場合に機能する行が折り返される場所を検出するためにJavaScriptライブラリを作りました: http://github.com/xdamman/js-line-wrap-detector

これがお役に立てば幸いです。

7
xdamman

各行をスパンでラップする必要がある状況があります。これを行うのは、テキストブロックにパディングされたハイライト効果を追加できるようにするためです。テキストをラップするスパンタグに背景を追加すると、テキストブロックの開始と終了のみが埋め込まれます。各行は個別にラップする必要があります。

これは上記の提案に基づいて私が思いついたものです:

$.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();
0
user3761817

内部マークアップと任意のフォントおよびスタイルがある場合にも機能する概念的にシンプルな方法は、すべてのWordを独自の要素(おそらく「SPAN」または「w」などのカスタム名)に入れる最初のパスを作成することです。

次に、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;
    }
}

遅いように聞こえますが、ドキュメントが本当に大きくない限り、あなたは気付かないでしょう。

0
TextGeek