Как я могу подсчитать текстовые строки внутри элемента DOM? Могу я?

Мне интересно, есть ли способ подсчета строк внутри div, например. Скажем, у нас есть div как:

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

В зависимости от многих факторов div может иметь одну или две или даже четыре строки текста. Есть ли способ для script знать?

Другими словами, автоматические разрывы, представленные в DOM вообще?

Ответ 1

Я убежден, что это невозможно сейчас. Это было, однако.

IE7s реализация 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 Honnens работает сегодня, потому что IE отображает ее в представлении совместимости с IE, нажмите F12 в IE8 для воспроизведения в разных режимах.)

Это печально. Похоже, что еще раз Firefox, буквально, но бесполезная реализация спецификации, полученной от Microsoft, была полезной. Или мне не хватает ситуации, когда новые getClientRects могут помочь разработчику?

Ответ 2

Если размер 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 divHeight = document.getElementById('content').offsetHeight
  var lineHeight = parseInt(document.getElementById('content').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>

Ответ 3

Одно из решений - заключить каждое слово в тег span с помощью script. Тогда, если размерность Y данного тега span меньше, чем у его ближайшего предшественника, тогда произошел разрыв строки.

Ответ 4

Для тех, кто использует 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);
}

Ответ 5

Я не был удовлетворен ответами здесь и по другим вопросам. Максимальный ответ не учитывает padding или border, и поэтому, очевидно, игнорирует также 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>

Ответ 6

Отметьте функцию getClientRects(), которая может использоваться для подсчета количества строк в элементе. Вот пример того, как его использовать.

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

Он возвращает объект DOM javascript. Количество строк можно узнать, выполнив следующее:

var amount_of_lines = message_lines.length;

Он может возвращать высоту каждой строки и многое другое. Посмотрите полный массив вещей, которые он может сделать, добавив это в свой script, а затем заглянув в свой консольный журнал.

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>

Хотя я не рекомендую жесткое кодирование такого стиля. Это просто для целей.

Ответ 7

Клонировать контейнерный объект и записать 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/

Ответ 8

Нет, не надежно. Существует слишком много неизвестных переменных

  • Какая ОС (разные DPI, варианты шрифтов и т.д.)?
  • У них размер шрифта увеличен, потому что они практически слепы?
  • Черт, в браузерах webkit вы можете реально изменить размер текстовых полей в своем сердце.

Список можно продолжить. Когда-нибудь я надеюсь, что будет такой способ надежного выполнения этого с помощью JavaScript, но до этого дня вам не повезет.

Я ненавижу подобные ответы, и я надеюсь, что кто-то может доказать, что я ошибаюсь.

Ответ 9

на основе ответа 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);
}

трюк здесь заключается в том, чтобы увеличить высоту строки настолько, что она "проглотит" любые различия между браузером и ОС в том, как они отображают шрифты

Проверено с помощью различных стилей и разных размеров шрифта/семей единственное, что он не принимает во внимание (поскольку в моем случае это не имело значения), это дополнение, которое можно легко добавить к решению.

Ответ 10

Вы должны иметь возможность разделить ('\n') длину и получить разрывы строк.

update: это работает в 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>

Ответ 11

Следуя предложению @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>

Ответ 12

getClientRects верните клиенту rects, как this, и если вы хотите получить строки, используйте следующую функцию, например this

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

Ответ 13

Я нашел способ рассчитать номер строки при разработке редактора html. Основной метод заключается в следующем:

  • В IE вы можете вызвать getBoundingClientRects, он возвращает каждую строку как прямоугольник

  • В webkit или новом стандартном движке html он возвращает каждый элемент или    node клиентских прямоугольников, в этом случае вы можете сравнить каждый   прямоугольники, я имею в виду, что каждый из них должен быть прямоугольником, самым большим, поэтому   вы можете игнорировать те прямоугольники, высота которых меньше (если есть   прямоугольник сверху меньше, а нижний больше, чем   условие истинно.)

поэтому посмотрим результат теста:

введите описание изображения здесь

Зеленый прямоугольник - самый большой прямоугольник в каждой строке

Красный прямоугольник - это граница выделения

Синий прямоугольник - это граница от начала до выбора после расширения, мы видим, что он может быть больше красного прямоугольника, поэтому мы должны проверить каждое нижнее дно прямоугольника, чтобы оно было меньше, чем красное нижнее дно прямоугольника.

        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

Ответ 14

Попробуйте это решение:

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/

Попробуйте изменить размеры панелей на странице (чтобы сделать правую сторону страницы более широкой или короче), а затем запустите ее снова, чтобы убедиться, что она может достоверно определить, сколько строк есть.

Эта проблема сложнее, чем кажется, но большая часть трудности исходит из двух источников:

  • Текстовый рендеринг слишком низкоуровневый в браузерах, чтобы напрямую запрашивать JavaScript. Даже псевдоселектор CSS:: first-line не ведет себя так же, как другие селекторы (вы не можете инвертировать его, например, применять стили к всем, кроме первой строки).

  • Контекст играет большую роль в том, как вы рассчитываете количество строк. Например, если строка высоты не была явно задана в иерархии целевого элемента, вы можете получить "нормальную" в качестве высоты строки. Кроме того, элемент может использовать box-sizing: border-box и, следовательно, подлежит заполнению.

Мой подход сводит к минимуму # 2, напрямую управляя прямой линией и факторизуя в методе калибровки ящика, что приводит к более детерминированному результату.

Ответ 15

Вы можете сравнить высоту элемента и высоту элемента с помощью 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
}