DOM要素内のテキスト行を数えるにはどうすればよいですか?できますか?


127

たとえば、div内の行をカウントする方法があるかどうか疑問に思っています。次のようなdivがあるとします。

<div id="content">hello how are you?</div>

多くの要因に応じて、divには1行、2行、または4行のテキストを含めることができます。スクリプトが知る方法はありますか?

言い換えれば、自動ブレークはDOMで表されますか?

回答:


89

divのサイズがコンテンツに依存している場合(説明からそうであると想定しています)、divの高さを次のように取得できます。

var divHeight = document.getElementById('content').offsetHeight;

フォントの行の高さで割ります:

document.getElementById('content').style.lineHeight;

または、明示的に設定されていない場合は行の高さを取得します。

var element = document.getElementById('content');
document.defaultView.getComputedStyle(element, null).getPropertyValue("lineHeight");

また、パディングと行間スペースも考慮する必要があります。

編集

完全に自己完結型のテスト、行の高さを明示的に設定:

function countLines() {
   var el = document.getElementById('content');
   var divHeight = el.offsetHeight
   var lineHeight = parseInt(el.style.lineHeight);
   var lines = divHeight / lineHeight;
   alert("Lines: " + lines);
}
<body onload="countLines();">
  <div id="content" style="width: 80px; line-height: 20px">
    hello how are you? hello how are you? hello how are you? hello how are you?
  </div>
</body>


7
行の高さが常に設定されているとは限らないことを除いて、これは良いスタートです。要素の計算されたスタイルからそれを取得する必要があります。
Chetan S

考え直してみると、行の高さは常に数値(またはピクセル単位)である必要はありません。したがって、コードがコードに依存し、CSS規則で許可されている場合は、行の高さをピクセル単位で設定する必要があります。
Chetan S

6
@Chetan-テキストの寸法をピクセルで設定することは、一般的に悪いことと考えられています。astahost.com/Sizes-Webdesign-Em-Vs-Px-t8926.html
annakata

6
これは、(私の例のように)最も単純な場合にのみ機能します。内部にスパン、インラインブロック要素などがある場合、(親)font-sizeによる単純な除算は無意味です。それでも、何もないよりはましです、ありがとう。
ブチオキサ

2
計算結果line-height(明示的に設定されていない)を取得するためのより良い方法は、次の方法を使用することだと思いますwindow.getComputedStyle(element, null).getPropertyValue('line-height')
rnevius

20

1つの解決策は、スクリプトを使用してすべての単語をspanタグで囲むことです。次に、特定のスパンタグのY寸法が直前のY寸法よりも小さい場合、改行が発生しています。


賢い!どうやってやるの?(例のように)テキストのみの段落を想定していると思います。私はこの制限を考慮していませんでしたが、段落内に何か他のものがある場合にスクリプトがクラッシュしない限り、問題はありません。
ブチオキサ

1
考え直してみると、ラインに縦に並んだスパンがある場合、このメソッドは失敗します。そのため、テキストのみの段落でも誤ってカウントされる可能性があります。
buti-oxa

私のdivにはテキストと画像があります...幸い、画像は完全に配置されているので、除外されると思います。:Dとにかく私はあなたが提案したのと同じコードを使用しましたが、divではなくスパンを使用していますが、+ 1をありがとう!
Albert Renshaw 2013年

20

要素の行数をカウントするために使用できる関数getClientRects()を確認してください。使い方の例を以下に示します。

var message_lines = $("#message_container")[0].getClientRects();

javascript DOMオブジェクトを返します。行数は次のようにして知ることができます:

var amount_of_lines = message_lines.length;

各行の高さなどを返すことができます。これをスクリプトに追加し、コンソールログを調べることで、実行できるすべてのことを確認できます。

console.log("");
console.log("message_lines");
console.log(".............................................");
console.dir(message_lines);
console.log("");

いくつかの注意点は、それが含まれている要素がインラインの場合にのみ機能することですが、含まれているインライン要素をブロック要素で囲んで幅を制御することもできます。

<div style="width:300px;" id="block_message_container">
<div style="display:inline;" id="message_container">
..Text of the post..
</div>
</div>

そのようなスタイルをハードコーディングすることはお勧めしませんが。これは単なる例です。


3
これと他のgetClientRectsベースの回答は、受け入れられた回答よりもはるかに優れています
matteo

2
これは受け入れられる答えになるはずです。ありがとう@ Digital-Christie
Antuan

12

ここや他の質問に対する答えに満足できませんでした。最高評価の回答は考慮されないpaddingborder、考慮されないため、明らかに無視さbox-sizingれます。私の答えは、ここと他のスレッドでいくつかのテクニックを組み合わせて、私の満足に機能するソリューションを取得します。

完璧ではありません:(line-height例:normalまたはinherit)の数値を取得できなかった場合は、font-size乗算されたを使用し1.2ます。おそらく他の誰かがそれらの場合にピクセル値を検出する信頼できる方法を提案することができます。

それ以外は、私が投げつけたほとんどのスタイルとケースを正しく処理できました。

jsFiddleで遊んでテストしてください。以下もインライン。

function countLines(target) {
  var style = window.getComputedStyle(target, null);
  var height = parseInt(style.getPropertyValue("height"));
  var font_size = parseInt(style.getPropertyValue("font-size"));
  var line_height = parseInt(style.getPropertyValue("line-height"));
  var box_sizing = style.getPropertyValue("box-sizing");
  
  if(isNaN(line_height)) line_height = font_size * 1.2;
 
  if(box_sizing=='border-box')
  {
    var padding_top = parseInt(style.getPropertyValue("padding-top"));
    var padding_bottom = parseInt(style.getPropertyValue("padding-bottom"));
    var border_top = parseInt(style.getPropertyValue("border-top-width"));
    var border_bottom = parseInt(style.getPropertyValue("border-bottom-width"));
    height = height - padding_top - padding_bottom - border_top - border_bottom
  }
  var lines = Math.ceil(height / line_height);
  alert("Lines: " + lines);
  return lines;
}
countLines(document.getElementById("foo"));
div
{
  padding:100px 0 10% 0;
  background: pink;
  box-sizing: border-box;
  border:30px solid red;
}
<div id="foo">
x<br>
x<br>
x<br>
x<br>
</div>


役立つ投稿...ありがとう
Sinto

適切な考慮事項ですが、私の場合、1行のdivの高さは行の高さよりも1px大きく、パディングや境界線はありません。そのため、回答と@ e-info128の回答(cloneNodeメソッド)を組み合わせて行の高さを取得する方が適切な選択かもしれません
Eric

8

コンテナーオブジェクトのクローンを作成し、2文字を書き込んで高さを計算します。これは、すべてのスタイルが適用された実際の高さ、行の高さなどを返します。次に、高さオブジェクト/文字のサイズを計算します。Jqueryでは、高さはパディング、マージン、ボーダーを上回ります。各行の実際の高さを計算すると便利です。

other = obj.clone();
other.html('a<br>b').hide().appendTo('body');
size = other.height() / 2;
other.remove();
lines = obj.height() /  size;

各文字の高さが異なる珍しいフォントを使用すると、これは機能しません。ただし、Arial、モノラル、コミック、Verdanaなどのすべての通常のフォントで機能します。フォントをテストしてください。

例:

<div id="content" style="width: 100px">hello how are you? hello how are you? hello how are you?</div>
<script type="text/javascript">
$(document).ready(function(){

  calculate = function(obj){
    other = obj.clone();
    other.html('a<br>b').hide().appendTo('body');
    size = other.height() / 2;
    other.remove();
    return obj.height() /  size;
  }

  n = calculate($('#content'));
  alert(n + ' lines');
});
</script>

結果: 6 Lines

すべてのブラウザで動作し、標準外のまれな機能はありません。

チェック:https : //jsfiddle.net/gzceamtr/


まず初めに、私が探していたものに感謝します。計算機能の各行について説明してください。よろしくお願いします。SYA :)
LebCit 2017年

1
jQuery以外の回答が必要な場合:clonecloneNodehidestyle.visibility = "hidden"html('a<br>b')textContent='a\r\nb'appendTo('body')document.documentElement.appendChildheight()getBoundingClientRect().height
Eric

行の高さとdivの高さの間に1pxのエラーが出るので、この回答をお勧めします。これと@Jeffの答えを組み合わせるとさらに良くなります
Eric

6

jQuery http://jsfiddle.net/EppA2/3/を使用する人向け

function getRows(selector) {
    var height = $(selector).height();
    var line_height = $(selector).css('line-height');
    line_height = parseFloat(line_height)
    var rows = height / line_height;
    return Math.round(rows);
}

jsfiddle.net/EppA2/9はあまり正確であり、それに応じて、このより良い様々なブラウザでサポートされてもよい
penkovsky

8
jQueryがから「通常」を返す場合$(selector).css('line-height');、その関数から数値を取得することはできません。
bafromca 2014

5

今は不可能だと私は確信しています。でもそうだった。

IE7のgetClientRectsの実装は、まさに私が望んでいたことを行いました。開いたこのページをIE8で、さまざまなウィンドウ幅を更新して、最初の要素の行数がそれに応じてどのように変化するかを確認してください。そのページのJavaScriptの重要な行は次のとおりです。

var rects = elementList[i].getClientRects();
var p = document.createElement('p');
p.appendChild(document.createTextNode('\'' + elementList[i].tagName + '\' element has ' + rects.length + ' line(s).'));

私にとって残念なことに、Firefoxは常に要素ごとに1つのクライアント四角形を返しますが、IE8は同じことを行います。(Martin Honnenのページは、IEがIE互換ビューで表示するため、今日機能します。IE8でF12を押して、さまざまなモードで再生します。)

これは悲しいです。再びFirefoxのリテラルのように見えますが、仕様の無価値な実装がMicrosoftの便利な仕様に勝っています。または、新しいgetClientRectsが開発者に役立つ可能性がある状況を見逃していますか?


2
Mozillaのelement.getClientRectsのドキュメントで指摘されているように、少なくともインライン要素の場合、W3C仕様でテキストの各行にrectが生成されます—理想的ではなく、少なくとも何かです。
natevw 2012年

getClientRects()。lengthが正しい方法です。getComputedStyle()は、「継承」、「通常」、「自動」などの値を返す場合があります。
Binyamin

4

上からのGuyPaddockの答えに基づいて、これは私にとってはうまくいくようです

function getLinesCount(element) {
  var prevLH = element.style.lineHeight;
  var factor = 1000;
  element.style.lineHeight = factor + 'px';

  var height = element.getBoundingClientRect().height;
  element.style.lineHeight = prevLH;

  return Math.floor(height / factor);
}

ここでの秘訣は、ブラウザ/ OSの違いがフォントをレンダリングする方法で "飲み込む"ように行の高さを増やすことです。

さまざまなスタイリングとさまざまなフォントサイズ/ファミリで確認したところ、考慮されないのは(私の場合は問題ではないため)パディングです。これは簡単にソリューションに追加できます。


2

いいえ、確実ではありません。単に未知の変数が多すぎる

  1. どのOS(異なるDPI、フォントバリエーションなど...)?
  2. それらは実質的に盲目であるので、フォントサイズが拡大されていますか?
  3. 一体、Webkitブラウザーでは、実際にテキストボックスのサイズを思いのままに変更できます。

リストは続きます。いつかJavaScriptで確実にこれを達成するような方法があることを願っていますが、その日が来るまで、あなたは運が悪いです。

私はこれらの種類の答えが嫌いで、誰かが私の間違いを証明してくれることを願っています。


3
レンダリング後に線を数える場合、それらすべての未知数は無関係です。もちろん、ブラウザがテキストをレンダリングするときに使用する行数を「推測」しようとする場合は、この点が当てはまります。
サイモン、

ズームインしたり、テキストサイズを変更したりするユーザーの変数は無視できます。ウィンドウのスケーリング(ページが読み込まれた後)またはテキストサイズのブラウザーごとの変更に対応するように最適化されたデザインのサイトを見たことがありません。ただし、信頼できる解決策はなく、他の人が指摘したように、ピクセルに基づく「推測」だけであることに同意します。
Kalko 2016

2

split( '\ n')。lengthを実行して改行を取得できるはずです。

更新:これはFF / Chromeでは機能しますがIEでは機能しません。

<html>
<head>
<script src="jquery-1.3.2.min.js"></script>
<script>
    $(document).ready(function() {
        var arr = $("div").text().split('\n');
        for (var i = 0; i < arr.length; i++)
            $("div").after(i + '=' + arr[i] + '<br/>');
    });
</script>
</head>
<body>
<div>One
Two
Three</div>
</body>
</html>

1
それを試して、それがどうなるか教えてください。私は真面目で、皮肉ではありません。このページのFireBugのコンソールでこのコードを使用できます。var the_text = $( '。welovestackoverflow p')。text(); var numlines = the_text.split( "\ n")。length; alert(numlines);
KyleFarris 2009

3
この驚きのおかげで、それが可能であるとは知りませんでしたが、これは私が望んでいることではありません。jqueryはソースのハード改行を数えるようで、結果の自動改行に興味があります。"One Two Three" divの場合、ブラウザーはすべてのテキストを1行に入れるため、結果は1になります。
ブチオキサ

それから私はあなたの質問を誤解しました。次に、計算された行の高さを見つけます。
チャドグラント

1

getClientRects以下のようなクライアントrects返し、これを、あなたが行を取得したい場合は、同様にフォロー機能を使用して、この

function getRowRects(element) {
    var rects = [],
        clientRects = element.getClientRects(),
        len = clientRects.length,
        clientRect, top, rectsLen, rect, i;

    for(i=0; i<len; i++) {
        has = false;
        rectsLen = rects.length;
        clientRect = clientRects[i];
        top = clientRect.top;
        while(rectsLen--) {
            rect = rects[rectsLen];
            if (rect.top == top) {
                has = true;
                break;
            }
        }
        if(has) {
            rect.right = rect.right > clientRect.right ? rect.right : clientRect.right;
            rect.width = rect.right - rect.left;
        }
        else {
            rects.push({
                top: clientRect.top,
                right: clientRect.right,
                bottom: clientRect.bottom,
                left: clientRect.left,
                width: clientRect.width,
                height: clientRect.height
            });
        }
    }
    return rects;
}

1

HTMLエディターを開発するときに、行番号を計算する方法を見つけました。主な方法は次のとおりです。

  1. IEではgetBoundingClientRectsを呼び出すことができ、各行を長方形として返します

  2. Webkitまたは新しい標準HTMLエンジンでは、各要素またはノードのクライアントの四角形を返します。この場合、各四角形を比較できます。つまり、それぞれの四角形が最大でなければならないため、高さが小さい方の四角形を無視できます(if長方形の上部がそれよりも小さく、底部がそれよりも大きい場合、条件はtrueです。)

テスト結果を見てみましょう:

ここに画像の説明を入力してください

緑の長方形は各行の最大の長方形です

赤い長方形は選択境界です

青い長方形は、展開後の開始から選択までの境界です。赤い長方形よりも大きくなる可能性があるため、各長方形の底をチェックして、赤い長方形の底よりも小さくなければならないことを制限する必要があります。

        var lineCount = "?";
        var rects;
        if (window.getSelection) {
            //Get all client rectangles from body start to selection, count those rectangles that has the max bottom and min top
            var bounding = {};
            var range = window.getSelection().getRangeAt(0);//As this is the demo code, I dont check the range count
            bounding = range.getBoundingClientRect();//!!!GET BOUNDING BEFORE SET START!!!

            //Get bounding and fix it , when the cursor is in the last character of lineCount, it may expand to the next lineCount.
            var boundingTop = bounding.top;
            var boundingBottom = bounding.bottom;
            var node = range.startContainer;
            if (node.nodeType !== 1) {
                node = node.parentNode;
            }
            var style = window.getComputedStyle(node);
            var lineHeight = parseInt(style.lineHeight);
            if (!isNaN(lineHeight)) {
                boundingBottom = boundingTop + lineHeight;
            }
            else {
                var fontSize = parseInt(style.fontSize);
                if (!isNaN(fontSize)) {
                    boundingBottom = boundingTop + fontSize;
                }
            }
            range = range.cloneRange();

            //Now we have enougn datas to compare

            range.setStart(body, 0);
            rects = range.getClientRects();
            lineCount = 0;
            var flags = {};//Mark a flags to avoid of check some repeat lines again
            for (var i = 0; i < rects.length; i++) {
                var rect = rects[i];
                if (rect.width === 0 && rect.height === 0) {//Ignore zero rectangles
                    continue;
                }
                if (rect.bottom > boundingBottom) {//Check if current rectangle out of the real bounding of selection
                    break;
                }
                var top = rect.top;
                var bottom = rect.bottom;
                if (flags[top]) {
                    continue;
                }
                flags[top] = 1;

                //Check if there is no rectangle contains this rectangle in vertical direction.
                var succ = true;
                for (var j = 0; j < rects.length; j++) {
                    var rect2 = rects[j];
                    if (j !== i && rect2.top < top && rect2.bottom > bottom) {
                        succ = false;
                        break;
                    }
                }
                //If succ, add lineCount 1
                if (succ) {
                    lineCount++;
                }
            }
        }
        else if (editor.document.selection) {//IN IE8 getClientRects returns each single lineCount as a rectangle
            var range = body.createTextRange();
            range.setEndPoint("EndToEnd", range);
            rects = range.getClientRects();
            lineCount = rects.length;
        }
        //Now we get lineCount here

1

この解決策を試してください:

function calculateLineCount(element) {
  var lineHeightBefore = element.css("line-height"),
      boxSizing        = element.css("box-sizing"),
      height,
      lineCount;

  // Force the line height to a known value
  element.css("line-height", "1px");

  // Take a snapshot of the height
  height = parseFloat(element.css("height"));

  // Reset the line height
  element.css("line-height", lineHeightBefore);

  if (boxSizing == "border-box") {
    // With "border-box", padding cuts into the content, so we have to subtract
    // it out
    var paddingTop    = parseFloat(element.css("padding-top")),
        paddingBottom = parseFloat(element.css("padding-bottom"));

    height -= (paddingTop + paddingBottom);
  }

  // The height is the line count
  lineCount = height;

  return lineCount;
}

ここでそれを実際に見ることができます:https//jsfiddle.net/u0r6avnt/

ページのパネルのサイズを変更して(ページの右側を広くしたり短くしたり)、もう一度実行して、行数が確実にわかることを確認してください。

この問題は見かけよりも難しいですが、ほとんどの問題は2つの原因に起因します。

  1. ブラウザでのテキストレンダリングは低レベルすぎて、JavaScriptから直接クエリすることはできません。他のセレクタは、(あなたがすべてにスタイリング適用するには、例えば、それを反転することができないんかなりのようにもCSS :: first-line擬似セレクタは動作しませんが、最初の行)。

  2. 行数の計算方法では、コンテキストが大きな役割を果たします。たとえば、ターゲット要素の階層でline-heightが明示的に設定されていなかった場合、「通常」が行の高さとして返される可能性があります。さらに、要素が使用されている可能性があるbox-sizing: border-boxため、パディングが行われる可能性があります。

私のアプローチでは、行の高さを直接制御し、ボックスのサイズ設定方法を考慮に入れることにより、#2を最小限に抑え、より確定的な結果をもたらします。


1

正当化されていないテキストの複数の行にまたがるリンクのように、これを使用すると、行数と各行のすべての座標を取得できる場合があります。

var rectCollection = object.getClientRects();

https://developer.mozilla.org/en-US/docs/Web/API/Element/getClientRects

各行が少しでも異なるため、これは機能します。それらがある限り、レンダラーによって別の「長方形」として描画されます。


0

@BobBrunius 2010の提案に従って、jQueryでこれを作成しました。間違いなくそれは改善されるかもしれませんが、いくつかを助けるかもしれません。

$(document).ready(function() {

  alert("Number of lines: " + getTextLinesNum($("#textbox")));

});

function getTextLinesNum($element) {

  var originalHtml = $element.html();
  var words = originalHtml.split(" ");
  var linePositions = [];
        
  // Wrap words in spans
  for (var i in words) {
    words[i] = "<span>" + words[i] + "</span>";
  }
        
  // Temporarily replace element content with spans. Layout should be identical.
  $element.html(words.join(" "));
        
  // Iterate through words and collect positions of text lines
  $element.children("span").each(function () {
    var lp = $(this).position().top;
    if (linePositions.indexOf(lp) == -1) linePositions.push(lp);
  });
        
  // Revert to original html content
  $element.html(originalHtml);
        
  // Return number of text lines
  return linePositions.length;

}
#textbox {
  width: 200px;
  text-align: center;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div id="textbox">Lorem ipsum dolor sit amet, consectetuer adipiscing elit,
  <br>sed diam nonummy</div>


0

要素の高さと要素の高さを line-height: 0

function lineCount(elm) {
  const style = elm.getAttribute('style')
  elm.style.marginTop = 0
  elm.style.marginBottom = 0
  elm.style.paddingTop = 0
  elm.style.paddingBottom = 0
  const heightAllLine = elm.offsetHeight
  elm.style.lineHeight = 0
  const height1line = elm.offsetHeight
  const lineCount = Math.round(heightAllLine / height1line)
  elm.setAttribute('style', style)
  if (isNaN(lineCount)) return 0
  return lineCount
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.