Як я можу рахувати текстові рядки всередині елемента DOM? Можна я?


127

Мені цікаво, чи є спосіб, наприклад, порахувати рядки всередині div. Скажіть, у нас є такий дів:

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

Залежно від багатьох факторів, div може мати один, два, а то й чотири рядки тексту. Чи є сценарій знати?

Іншими словами, чи взагалі в 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");

Вам також потрібно врахувати прокладки та міжрядкові інтервали.

EDIT

Повністю автономний тест, чітко встановлюючи висоту рядка:

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/Size-Webdesign-Em-Vs-Px-t8926.html
annakata

6
Це може працювати лише в найпростішому випадку (як, наприклад, мій приклад). Якщо всередині є прольоти, елементи блоку вбудованого блоку тощо, прямий поділ на (батьківський) розмір шрифту не має сенсу. Все-таки краще, ніж нічого, дякую.
buti-oxa

2
Я думаю, що кращим методом отримання обчислених line-height(які явно не встановлені) було б використанняwindow.getComputedStyle(element, null).getPropertyValue('line-height')
rnevius

20

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


Розумний! Як ти це робиш? Я думаю, ви припускаєте лише абзац з текстом (як я маю в прикладі). У мене не було цього обмеження на увазі, але це може бути нормально, за умови, що сценарій не вийде з ладу, коли всередині абзацу є щось інше.
buti-oxa

1
По-друге, цей спосіб провалюється, якщо в лінії є вертикально вирівняні проміжки. Так, навіть абзац лише з тексту може бути зарахований неправильно.
buti-oxa

У мого діва є текст та зображення ... на щастя, зображення абсолютно розміщені, тому я думаю, що вони будуть виключені. : D У будь-якому разі я використовував той самий код, який ви запропонували, але з дівом не прольотом, дякую хоч +1!
Альберт Реншо

20

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

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

Він повертає DOM-об’єкт javascript. Кількість рядків можна дізнатися, виконавши це:

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 набагато краща за прийняту
Маттео

2
Це має бути прийнятою відповіддю. Спасибі @ Digital-Крісті
Антуан

12

Я не був задоволений відповідями тут та іншими питаннями. Відповідь з найбільшою оцінкою не враховує 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>


уважний пост ... Спасибі
Сінто

Добре враховуйте, але в моєму випадку висота одного рядкового ключа більше, ніж висота лінії на 1 пікс, без прокладки та облямівки. Тож отримання висоти рядка за допомогою комбінування вашої відповіді та відповіді @ e-info128 (метод cloneNode) може бути кращим вибором
Ерік,

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

1
для тих, хто потребує відповіді, що не відповідає jQuery: clonecloneNode, hidestyle.visibility = "hidden", html('a<br>b')textContent='a\r\nb', appendTo('body')document.documentElement.appendChild, height()getBoundingClientRect().height
Eric

Оскільки я отримую помилку 1px між висотою лінії та висотою div, я рекомендую цю відповідь. Поєднавши це і відповідь @ Джеффа, буде ще краще
Ерік,

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

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 робить те ж саме зараз. (Сторінка Мартіна Гоннена працює сьогодні, тому що IE відображає її в режимі IE compat; натисніть F12 в IE8, щоб грати в різних режимах.)

Це сумно. Схоже, вкотре буквальна, але нікчемна реалізація специфікацій Firefox, виграних над корисним Microsoft. Або я пропускаю ситуацію, коли нові getClientRects можуть допомогти розробнику?


2
Як було відзначено в Mozilla element.getClientRects документації, по крайней мере , для вбудованих елементів W3C специфікації робить результат в прямокутнику для кожного рядка тексту - не ідеал, але що - то , по крайней мере.
natevw

getClientRects (). довжина - правильний шлях. getComputedStyle () може повернути такі значення, як "успадкувати", "нормально" та "автоматично".
Біньямін

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. Чорт, у веб-браузерах, ви можете фактично змінити розмір текстових полів на ваше бажання.

Список продовжується. Колись я сподіваюся, що буде такий спосіб надійного досягнення цього JavaScript, але поки не настане цей день, вам не пощастить.

Я ненавиджу подібні відповіді і сподіваюся, що хтось може довести мене неправильно.


3
Якщо порахувати рядки після візуалізації, всі ці невідомі значення не мають. Ваша думка застосовується, звичайно, якщо ви спробуєте "вгадати", скільки рядків використовує браузер під час надання тексту.
симон

Я б сказав, що змінну користувачів, що збільшують та / або змінюють розмір тексту, можна ігнорувати. Я ніколи не бачив веб-сайту, на якому його дизайн оптимізований для розміщення вікон (після завантаження сторінки) або зміни розміру тексту в браузері, тому я не думаю, що це важливі фактори в цьому конкретному питанні. Однак я погоджуюся, що надійних рішень немає, просто "здогадки" на основі пікселів, як вказували інші.
Калько

2

Ви повинні мати змогу розділити довжину ('\ n').

оновлення: це працює на 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"). довжина; оповіщення (числові лінії);
KyleFarris

3
Дякую за цей надмір, я не знав, що це можливо, але це не те, що я хочу. Jquery, здається, рахує жорсткі перерви в джерелі, і мене цікавлять автоматичні розриви рядків у результаті. У випадку з вашим роздільником "Один два три" результат повинен бути 1, оскільки браузер вводить весь текст в один рядок.
буті-окса

Тоді я неправильно зрозумів ваше запитання. Потім потрібно знайти обчислену висоту лінії.
Чад Грант

1

getClientRectsповернути клієнтські відповіді таким чином, і якщо ви хочете отримати рядки, скористайтеся функцією follow так, як це

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. У веб-програмі чи новому стандартному механізмі 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. Контекст відіграє велику роль у тому, як ви обчислюєте кількість рядків. Наприклад, якщо висота рядка не була явно встановлена ​​в ієрархії цільового елемента, ви можете отримати "нормальну" назад як висоту рядка. Крім того, елемент може бути використаний 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
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.