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

Posted

技术标签:

【中文标题】如何计算 DOM 元素内的文本行数?我可以吗?【英文标题】:How can I count text lines inside an DOM element? Can I? 【发布时间】:2010-10-21 11:58:13 【问题描述】:

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

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

根据许多因素,div 可以有一行、两行甚至四行文本。脚本有什么办法知道吗?

换句话说,自动中断是否在 DOM 中表示?

【问题讨论】:

????我非常简单有效的方法 - codepen.io/vsync/pen/BawdgEd 【参考方案1】:

如果 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>

【讨论】:

这是一个好的开始,除了 line-height 可能并不总是被设置。你应该从元素的计算样式中得到它。 再想一想,行高不必总是数字(或以像素为单位)。因此,如果您的代码依赖于它并且您的 CSS 约定允许它,您可能应该以像素为单位设置行高。 @Chetan - 以像素为单位设置文本尺寸通常被认为是一件坏事。 astahost.com/Sizes-Webdesign-Em-Vs-Px-t8926.html 这可能仅适用于最简单的情况(如我的示例)。如果内部有 span、inline-block 元素等,直接按(父)字体大小划分是没有价值的。不过,总比没有好,谢谢。 我认为获得计算的line-height(未明确设置)的更好方法是使用window.getComputedStyle(element, null).getPropertyValue('line-height')【参考方案2】:

查看函数getClientRects(),它可用于计算元素中的行数。这是一个如何使用它的示例。

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

它返回一个 javascript DOM 对象。这样做可以知道行数:

var amount_of_lines = message_lines.length;

需要注意的一点是,它仅在包含元素是内联时才有效,但是您可以用块元素围绕包含内联元素来控制宽度,如下所示:

console.log(  message_container.getClientRects().length  )
<div style="display:inline;" id="message_container">
  ..Text of the post..<br>
  nice ha?
</div>

虽然我不建议像这样对样式进行硬编码。仅供参考。

【讨论】:

这个和其他基于 getClientRects 的答案比公认的要好得多 这应该是公认的答案。谢谢@Digital-Christie getClientRects().length 在 Opera 和 Chrome 中总是为我返回 1 @klm123 需要将元素设置为display: inline 添加到这个,如果你需要找到嵌套元素时的行数(astrong等)试试这个codesandbox.io/s/adoring-wind-v6s6h【参考方案3】:

一种解决方案是使用脚本将每个单词包含在 span 标记中。然后,如果给定跨度标签的 Y 维度小于其直接前一个标签的 Y 维度,则发生换行。

【讨论】:

聪明!你是怎样做的?我猜你假设只有文本段落(就像我在例子中一样)。我没有想到这个限制,但它可能没问题,前提是当段落中有其他内容时脚本不会崩溃。 再想一想,如果行中有垂直对齐的跨度,此方法将失败。因此,即使是纯文本段落也可能会被错误计算。 我的 div 有文本和图像......幸运的是图像是绝对定位的,所以我认为它们会被排除在外。 :D 无论如何,我使用了你建议的相同代码,但 div 不是跨度,谢谢 +1!【参考方案4】:

我对这里和其他问题的答案不满意。评分最高的答案没有考虑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>

【讨论】:

有帮助的帖子...谢谢 很好的考虑,但在我的情况下,一行 div 的高度比行高多 1px,没有任何填充和边框。所以通过结合你的答案和@e-info128 的答案(cloneNode 方法)来获得行高可能是一个更好的选择【参考方案5】:

克隆容器对象并写2个字母并计算高度。这将返回应用了所有样式、行高等的实际高度。现在,计算高度对象/字母的大小。在 Jquery 中,高度超过了 padding、margin 和border,计算每一行的真实高度非常棒:

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

如果您使用每个字母高度不同的稀有字体,这将不起作用。但适用于所有普通字体,如 Arial、mono、comics、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 :) 对于那些需要非jQuery的回答:clonecloneNodehidestyle.visibility = "hidden"html('a&lt;br&gt;b')textContent='a\r\nb'appendTo('body')document.documentElement.appendChild,@ 987654333@→getBoundingClientRect().height 因为我在行高和 div 高度之间得到 1px 的误差,所以我推荐这个答案。结合这个和@Jeff的回答会更好【参考方案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 不太准确,但根据this 可能会更好地被各种浏览器支持 当 jQuery 从 $(selector).css('line-height'); 返回“正常”时,您不会从该函数中得到数字。【参考方案7】:

我相信现在这是不可能的。不过确实如此。

IE7 的 getClientRects 实现正是我想要的。在 IE8 中打开this page,尝试根据不同的窗口宽度刷新它,并查看第一个元素中的行数如何相应变化。以下是该页面中 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 可以帮助开发人员的情况?

【讨论】:

正如 Mozilla 的 element.getClientRects 文档所指出的,至少对于 inline 元素,W3C 规范 确实 会为每一行文本生成一个矩形— 不理想,但至少是这样。 getClientRects().length 是正确的方法。 getComputedStyle() 可能返回“继承”、“正常”和“自动”等值。【参考方案8】:

根据上面 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);

这里的诀窍是增加行高,以至于它会“吞下”任何浏览器/操作系统在渲染字体的方式上的差异

使用各种样式和不同的字体大小/系列对其进行检查 唯一没有考虑到的(因为在我的情况下它无关紧要)是填充 - 可以很容易地添加到解决方案中。

【讨论】:

【参考方案9】:

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

    什么操作系统(不同的 DPI、字体变化等...)? 他们的字体大小是否因为几乎失明而放大? 哎呀,在 webkit 浏览器中,您实际上可以根据自己的意愿调整文本框的大小。

名单还在继续。我希望有一天会有这样一种方法可以用 JavaScript 可靠地完成此任务,但在那一天到来之前,你的运气不好。

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

【讨论】:

如果你计算渲染后的行数,所有这些未知数都无关紧要。当然,如果您尝试“猜测”浏览器在呈现文本时使用了多少行,那么您的观点也适用。 我想说用户放大和/或更改文本大小的变量可以忽略不计。我从未见过一个网站对其设计进行了优化以适应窗口缩放(加载页面后)或更改浏览器的文本大小,所以我认为这些不是这个特定问题的重要因素。但是我同意没有可靠的解决方案,只有其他人指出的基于像素的“猜测”。【参考方案10】:

你应该能够 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>

【讨论】:

试试看,告诉我们进展如何。我是认真的,不是讽刺。您可以在此页面上的 FireBug 控制台中使用此代码。 var the_text = $('.welove*** p').text(); var numlines = the_text.split("\n").length;警报(数字行); 感谢这个惊喜,我不知道这是可能的,但这不是我想要的。 Jquery 似乎计算了源代码中的硬换行符,我对结果中的自动换行符感兴趣。对于您的“一二三” div,结果应该是 1,因为浏览器将所有文本放在一行中。 那我误解了你的问题。然后你想找到计算出来的行高。【参考方案11】:
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;

【讨论】:

【参考方案12】:

我在开发 html 编辑器时找到了一种计算行号的方法。 主要方法是:

    在 IE 中您可以调用 getBoundingClientRects,它将每一行作为 矩形

    在 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

【讨论】:

【参考方案13】:

试试这个解决方案:

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/

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

这个问题比看起来要难,但大部分困难来自两个方面:

    浏览器中的文本呈现太低级,无法直接从 JavaScript 查询。甚至 CSS ::first-line 伪选择器的行为也不像其他选择器那样(例如,您不能反转它,以将样式应用于所有 第一行)。

    上下文在计算行数的方式中起着重要作用。例如,如果 line-height 没有在目标元素的层次结构中明确设置,您可能会得到“正常”作为行高。此外,该元素可能正在使用box-sizing: border-box,因此会受到填充。

我的方法通过直接控制行高并考虑盒子尺寸方法来最小化#2,从而产生更具确定性的结果。

【讨论】:

【参考方案14】:

在某些情况下,例如在非对齐文本中跨越多行的链接,当您使用此选项时,您可以获得行数和每行的每个坐标:

var rectCollection = object.getClientRects();

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

这是可行的,因为每一行都会略有不同。只要它们存在,它们就会被渲染器绘制为不同的“矩形”。

【讨论】:

【参考方案15】:

可以用line-height: 0比较元素高度和元素高度

function lineCount(elm) 
      const originalStyle = elm.getAttribute('style')
      
      // normalize 
      elm.style.padding = 0
      elm.style.border = 0
      
      // measure
      elm.style.lineHeight = 1
      const totalHeight = elm.offsetHeight
      elm.style.lineHeight = 0
      const singleLineHeight = elm.scrollHeight * 2
      
      const lineCount = Math.round(totalHeight / singleLineHeight)
      
      // undo above style changes
      elm.setAttribute('style', originalStyle)
      
      return (isNaN(lineCount) || singleLineHeight == 0) ? 0 : lineCount


function printElmLineCount(elm)
  console.log(
     lineCount(elm)
  )
p border:2em black solid ; padding:1em; line-height: 3em; 
<p contentEditable id='elm'>
  one<br>
  two<br>
  three
</p>

<button onclick='printElmLineCount(elm)'>Get lines count</button>

【讨论】:

【参考方案16】:

按照@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>

【讨论】:

【参考方案17】:

您可以计算元素的innerText 中的换行数,如下所示:

const text = anyDivElement.innerText;
const lines = text.split(/\r\n|\r|\n/).length;

【讨论】:

【参考方案18】:

最简单的方法是计算行高并除以元素高度。 此代码适用于任何种类的元素:

function getStyle(el,styleProp)

    var x = el;
    if (x.currentStyle)
        var y = x.currentStyle[styleProp];
    else if (window.getComputedStyle)
        var y = document.defaultView.getComputedStyle(x,null).getPropertyValue(styleProp);
    return y;

function calculateLineHeight (element) 

  var lineHeight = parseInt(getStyle(element, 'line-height'), 10);
  var clone;
  var singleLineHeight;
  var doubleLineHeight;

  if (isNaN(lineHeight)) 
    clone = element.cloneNode();
    clone.innerHTML = '<br>';
    element.appendChild(clone);
    singleLineHeight = clone.offsetHeight;
    clone.innerHTML = '<br><br>';
    doubleLineHeight = clone.offsetHeight;
    element.removeChild(clone);
    lineHeight = doubleLineHeight - singleLineHeight;
  

  return lineHeight;


function getNumlines(el)return Math.ceil(el.offsetHeight / calculateLineHeight (el))



console.log(getNumlines(document.getElementById('g1')))
.Textfont-size: 28px;
@media screen and (max-width: 780px) 
            .Textfont-size: 50px;
        
<div><span class="Text" id="g1" >
                This code works for any Kind of elements: bla blo bli bla blo bli bla blo bli bla blo bli bla blo bli bla blo bli bla blo bli bla blo bli bla blo bli bla blo bli bla blo bli bla blo bli bla blo bli bla blo bli bla blo bli bla blo bli bla blo bli bla blo bli bla blo bli </span>
        </div>

【讨论】:

以上是关于如何计算 DOM 元素内的文本行数?我可以吗?的主要内容,如果未能解决你的问题,请参考以下文章

根据javascript中的文本行数更改textarea的高度[重复]

如何计算每个目录中的文件数?

计算每个组内的元素数

如何从包装的文本单元格中提取特定的文本行?

如何在文件顶部添加新的文本行?

什么是保存从图像分割的文本行的matlab代码