如何计算DOM元素内的文本行?我可以吗?


127

我想知道是否有一种方法可以计算div中的行数。假设我们有一个像这样的div:

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

取决于许多因素,div可以包含一行,两行或什至四行文本。脚本有什么办法知道吗?

换句话说,DOM中是否完全代表了自动中断?

Answers:


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
这可能仅在最简单的情况下有效(例如我的示例)。如果内部有跨度,内联块元素等,那么直接按(父)字体大小进行分割就毫无价值。仍然,总比没有好,谢谢。
buti-oxa

2
我认为,获得计算line-height(未明确设置)的更好方法是使用window.getComputedStyle(element, null).getPropertyValue('line-height')
rnevius

20

一种解决方案是使用脚本将每个单词括在span标签中。然后,如果给定的span标签的Y尺寸小于其前一个标签的Y尺寸,则发生换行。


聪明!你是怎样做的?我猜您假设仅是文本段落(如示例中所示)。我没有考虑到此限制,但是如果段落中没有其他内容时脚本不会崩溃,则可以确定。
buti-oxa 2010年

1
再次考虑,如果线上有垂直对齐的跨度,则此方法将失败。因此,即使是纯文字段落也可能被错误地计算。
buti-oxa 2010年

我的div有文字和图片...幸运的是图片的位置绝对正确,因此我认为它们会被排除在外。:D无论如何,我使用了您建议的相同代码,但div不是跨度,尽管+1还是谢谢您!
阿尔伯特·伦肖

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("");

尽管需要注意的几件事是,它仅在containing元素为内联时才有效,但是您可以使用block元素将containing inline元素包围起来,以控制宽度,如下所示:

<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(例如normalinherit)获取数值时,它仅使用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

请考虑,但在我的情况下,一格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,mono,Comic,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/


首先,我要非常感谢您,正是我想要的。您能否解释一下calculate函数的每一行。非常感谢。SYA :)
LebCit

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是不太准确的,但根据这个可以通过各种浏览器更好的支持
潘科夫斯基

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总是为每个元素返回一个客户端矩形,而IE8现在也是如此。(Martin Honnen的页面今天可用,因为IE在IE兼容视图中呈现它;在IE8中按F12可以使用不同的模式播放。)

这真是难过;这真是伤心。看起来Firefox再次实现了字面意义,但毫无价值的实现赢得了微软有用的支持。还是我错过了新的getClientRects可以帮助开发人员的情况?


2
正如Mozilla的element.getClientRects文档所指出的那样,至少对于内联元素,W3C规范的确会为每一行文本生成一个rect,这不是理想的,至少是某些东西。
natevw 2012年

getClientRects()。length是正确的方法。getComputedStyle()可能会返回“ inherit”,“ normal”和“ auto”之类的值。
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);
}

这里的技巧是增加行高,以至于“吞噬”浏览器/操作系统在呈现字体方面的任何差异

使用各种样式和不同的字体大小/系列来检查它,只是没有考虑到的事情(因为在我的情况下没关系)是填充-可以轻松将其添加到解决方案中。


2

不,不可靠。太多未知变量

  1. 什么操作系统(不同的DPI,字体变化等)?
  2. 是否因为实际上是盲人而放大了字体大小?
  3. 哎呀,在Webkit浏览器中,您实际上可以调整文本框的大小以适应您的内心需求。

清单继续。希望有一天,会有这样一种用JavaScript可靠地完成此任务的方法,但是直到那一天到来之前,您都运气不好。

我讨厌这些答案,希望有人能证明我错了。


3
如果在渲染后计算行数,则所有这些未知数均无关紧要。当然,如果您尝试“猜测”呈现文本时浏览器使用了多少行,那么您的观点就适用。
simon 2013年

我想说的是,用户放大和/或更改文本大小的变量可以忽略。我从未见过这样一个网站,该网站的设计经过优化,以适应窗口缩放(在页面加载后)或在浏览器中更改文本大小,因此,我认为这些不是这个特定问题中的重要因素。但是我同意没有可靠的解决方案,只是其他人指出的基于像素的“猜测”。
卡尔科

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; 警报(数字);
KyleFarris

3
感谢您的惊讶,我不知道有可能,但这不是我想要的。jQuery似乎在源中计算硬换行符,我对结果中的自动换行符感兴趣。对于“一二三” div,结果应为1,因为浏览器将所有文本都放在一行中。
buti-oxa

然后我误解了你的问题。您要查找计算的行高。
乍得格兰特

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引擎中,它将返回每个元素或节点的客户端矩形,在这种情况下,您可以比较每个矩形,我的意思是每个矩形必须有一个最大的矩形,因此您可以忽略高度较小的那些矩形(如果有一个矩形的顶部小于它,底部大于它,条件为真。)

因此,让我们看一下测试结果:

在此处输入图片说明

绿色矩形是每行中最大的矩形

红色矩形是选择边界

蓝色矩形是展开后从开始到选择的边界,我们看到它可能大于红色矩形,因此我们必须检查每个矩形的底部以限制它必须小于红色矩形的底部。

        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/

尝试调整页面上面板的大小(使页面右侧变宽或变短),然后再次运行以查看它可以可靠地确定有多少行。

这个问题比看起来要难,但是大多数困难来自两个方面:

  1. 文本渲染在浏览器中的级别太低,无法直接从JavaScript查询。即使是CSS ::一线伪选择不表现得很像其他选择做(你不能反其道而行,例如,将样式应用到所有第一行)。

  2. 上下文在计算行数方面起着很大的作用。例如,如果未在目标元素的层次结构中显式设置line-height,则可能会将“ normal”作为行高返回。另外,该元素可能正在使用中box-sizing: border-box,因此可能需要填充。

我的方法通过直接控制行高并将框大小调整方法考虑在内,从而最大程度地减少了#2,从而获得了更具确定性的结果。



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
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.