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

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

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

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

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

160916 次浏览

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

不,不可靠,未知因素太多了

  1. 什么操作系统(不同的 DPI、字体变化等等) ?
  2. 他们是否因为实际上是盲人而把字体放大?
  3. 见鬼,在 webkit 浏览器中,您实际上可以根据自己的需要调整文本框的大小。

还有很多。我希望有一天能有这样一种可靠的方法来使用 JavaScript 来实现这一点,但是在那一天到来之前,你的运气不佳。

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

您应该能够分割(’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>

我确信现在这是不可能的,尽管曾经是。

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 compat 视图中呈现它; 在 IE8中按 F12可以使用不同的模式。)

真可悲。这看起来又一次像 Firefox 字面上但毫无价值的规范实现战胜了微软的有用规范。或者我错过了一个新的 getClientRects 可以帮助开发人员的情况?

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

对于那些使用 jQueryhttp://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);
}

查看函数 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>

虽然我不推荐这种风格的硬编码,它只是为了例子的目的。

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

返回类似于 这个的客户端矩形,如果你想得到行,使用像 这个这样的函数

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

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

  1. 在 IE 中,您可以调用 getBoundingClientRects,它将每一行返回为 矩形

  2. 在 webkit 或新的标准 html 引擎中,它返回每个元素或 节点的客户端矩形,在这种情况下,您可以比较每个 矩形,我的意思是每个矩形必须有一个最大的,所以 您可以忽略那些高度较小的矩形(如果有一个 矩形的顶部比它小,底部比它大 条件为真。)

让我们看看测试结果:

enter image description here

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

红色矩形是选择边界

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

        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

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

试试这个办法:

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: : first-line 伪选择器的行为也不像其他选择器那样(例如,您不能将其反转以将样式应用于所有 但是的第一行)。

  2. 上下文在如何计算行数方面起着很大的作用。例如,如果在目标元素的层次结构中没有显式地设置 line-height,那么您可能会得到“ Normal”作为 line height 返回。此外,元素可能使用 box-sizing: border-box,因此需要填充。

我的方法通过直接控制线条高度并在方框大小调整方法中考虑因素来最小化 # 2,从而得到更确定的结果。

克隆容器对象并写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,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/

基于盖伊帕多克上面的回答,这似乎对我有用

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

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

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

可以将元素高度和元素高度与 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>

在某些情况下,比如在不对齐的文本中跨越多行的链接,当您使用以下命令时,您可以得到每行的行数和每个坐标:

var rectCollection = object.getClientRects();

Https://developer.mozilla.org/en-us/docs/web/api/element/getclientrects

这种方法是有效的,因为每一行都会有细微的差别。只要它们存在,渲染器就会将它们绘制为不同的“矩形”。

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

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

最简单的方法是计算行高,然后除以元素高度。 这段代码适用于任何类型的元素:

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')))
.Text{font-size: 28px;}
@media screen and (max-width: 780px) {
.Text{font-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>

我的解决方案(可能是重复的) : 使这个元素现在换行计算 line Height,然后通过 oneLineHeight/lineHeight 计算 lineNumber

function getLineInfo(root: HTMLElement): {
lineNumber: number;
lineHeight: number;
elHeight: number;
} {
const oldOverFlow = root.style.overflow;
const oldWhiteSpace = root.style.whiteSpace;


root.style.overflow = "hidden";
root.style.whiteSpace = "nowrap";


const lineHeight = root.offsetHeight;
root.style.overflow = oldOverFlow;
root.style.whiteSpace = oldWhiteSpace;


const lineNumber = Math.round(root.offsetHeight / lineHeight);


return {
lineNumber: lineNumber,
lineHeight,
elHeight: root.offsetHeight,
};
}

另一个简单的解决方案,通过使用 getClientRects () ,没有经过很好的测试:

export function getLineInfo(root: HTMLElement): {
lineNumber: number;
lineHeight: number;
elHeight: number;
} {
const test = document.createElement("span");
test.textContent = " ";


root.appendChild(test);
const first = test.getClientRects();
root.insertBefore(test, root.firstChild);
const second = test.getClientRects();


const lineHeight = first[0].y - second[0].y;


test.remove();


const lastPadding = lineHeight - first[0].height;
const offsetHeight = first[0].bottom - second[0].top + lastPadding;


const lineNumber = Math.round(offsetHeight / lineHeight);


return {
lineNumber: lineNumber,
lineHeight,
elHeight: offsetHeight,
};
}