Jak policzyć wiersze tekstu wewnątrz elementu DOM? Czy mogę?


127

Zastanawiam się, czy na przykład istnieje sposób zliczania wierszy wewnątrz elementu div. Powiedzmy, że mamy taki div:

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

W zależności od wielu czynników element div może mieć jeden, dwa, a nawet cztery wiersze tekstu. Czy jest jakiś sposób, aby skrypt się dowiedział?

Innymi słowy, czy automatyczne przerwy są w ogóle reprezentowane w DOM?

Odpowiedzi:


89

Jeśli rozmiar div zależy od zawartości (co, jak zakładam, jest w przypadku twojego opisu), możesz pobrać wysokość div za pomocą:

var divHeight = document.getElementById('content').offsetHeight;

I podziel przez wysokość linii czcionki:

document.getElementById('content').style.lineHeight;

Lub, aby uzyskać wysokość linii, jeśli nie została wyraźnie ustawiona:

var element = document.getElementById('content');
document.defaultView.getComputedStyle(element, null).getPropertyValue("lineHeight");

Konieczne będzie również uwzględnienie dopełnienia i odstępów między wierszami.

EDYTOWAĆ

W pełni samodzielny test, z jawnym ustawieniem wysokości linii:

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
To dobry początek, z wyjątkiem tego, że wysokość linii nie zawsze była ustawiona. Powinieneś to uzyskać z wyliczonego stylu elementu.
Chetan S

Po drugie, wysokość linii nie zawsze musi być liczbowa (lub w pikselach). Więc jeśli twój kod opiera się na tym i twoja konwencja CSS na to pozwala, powinieneś prawdopodobnie ustawić wysokość linii w pikselach.
Chetan S

6
@Chetan - ustawienie wymiarów tekstu w pikselach jest ogólnie uważane za złe. astahost.com/Sizes-Webdesign-Em-Vs-Px-t8926.html
annakata

6
Może to działać tylko w najprostszym przypadku (jak mój przykład). Jeśli w środku znajdują się rozpiętości, elementy blokowe itp., Proste dzielenie przez rozmiar czcionki (nadrzędnej) jest bezwartościowe. Mimo wszystko to lepsze niż nic, dzięki.
buti-oxa

2
Myślę, że lepszą metodą uzyskania obliczonego line-height(który nie jest jawnie ustawiony) byłoby użyciewindow.getComputedStyle(element, null).getPropertyValue('line-height')
rnevius

20

Jednym z rozwiązań jest umieszczenie każdego słowa w tagu span za pomocą skryptu. Następnie, jeśli wymiar Y danego znacznika rozpiętego jest mniejszy niż wymiar jego bezpośredniego poprzednika, to nastąpiło przerwanie wiersza.


Sprytny! Jak to robisz? Domyślam się, że zakładasz tylko tekst (tak jak w przykładzie). Nie miałem na myśli tego ograniczenia, ale może być OK, pod warunkiem, że skrypt nie zawiesza się, gdy w akapicie jest coś innego.
buti-oxa

1
Po drugie, ta metoda zawodzi, jeśli w linii są ustawione pionowo rozpiętości. Tak więc nawet tekst zawierający tylko akapit może zostać nieprawidłowo policzony.
buti-oxa

Mój div ma tekst i obrazy ... na szczęście obrazy są pozycjonowane absolutnie, więc myślę, że zostaną wykluczone. : D W każdym razie użyłem tego samego kodu, który zasugerowałeś, ale z elementem div, a nie rozpiętością, ale dziękuję +1!
Albert Renshaw,

20

Sprawdź funkcję getClientRects (), której można użyć do zliczenia liczby wierszy w elemencie. Oto przykład, jak go używać.

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

Zwraca obiekt DOM javascript. Liczbę linii można poznać, wykonując następujące czynności:

var amount_of_lines = message_lines.length;

Może zwrócić wysokość każdej linii i więcej. Zobacz pełną gamę rzeczy, które może zrobić, dodając to do skryptu, a następnie przeglądając dziennik konsoli.

console.log("");
console.log("message_lines");
console.log(".............................................");
console.dir(message_lines);
console.log("");

Chociaż należy zwrócić uwagę na kilka rzeczy, to działa tylko wtedy, gdy element zawierający element jest wbudowany, jednak możesz otoczyć zawierający element wbudowany elementem blokowym, aby kontrolować szerokość w następujący sposób:

<div style="width:300px;" id="block_message_container">
<div style="display:inline;" id="message_container">
..Text of the post..
</div>
</div>

Chociaż nie polecam twardego kodowania stylu w ten sposób. To tylko przykładowe cele.


3
Ta i inna odpowiedź oparta na getClientRects jest znacznie lepsza niż zaakceptowana
matteo

2
To powinna być akceptowana odpowiedź. Dziękuję @ Digital-Christie
Antuan

12

Nie byłem zadowolony z odpowiedzi tutaj i innych pytań. Najwyższą ocenę odpowiedź nie bierze paddinglub borderpod uwagę, a zatem oczywiście ignoruje box-sizingrównież. Moja odpowiedź łączy kilka technik tutaj i na innych wątkach, aby uzyskać rozwiązanie, które działa w moim stopniu.

To nie jest idealne: gdy nie można było uzyskać żadnej wartości liczbowej dla line-height(np. normalLub inherit), używa po prostu font-sizepomnożenia przez 1.2. Być może ktoś inny może zasugerować niezawodny sposób wykrywania wartości piksela w takich przypadkach.

Poza tym był w stanie poprawnie obsłużyć większość stylów i przypadków, które mu rzuciłem.

jsFiddle do zabawy i testowania. Również poniżej.

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>


pomocny post ... Dziękuję
Sinto

Dobra uwaga, ale w moim przypadku wysokość jednego elementu div jest większa niż wysokość wiersza o 1 piksel, bez dopełnienia i obramowania. Więc uzyskanie wysokości wiersza przez przeczesanie odpowiedzi i odpowiedzi @ e-info128 (metoda cloneNode) może być lepszym wyborem
Eric

8

Sklonuj obiekt kontenera, napisz 2 litery i oblicz wysokość. Zwraca to rzeczywistą wysokość z zastosowanymi wszystkimi stylami, wysokością linii itp. Teraz oblicz wysokość obiektu / rozmiar litery. W Jquery wysokość przewyższa wypełnienie, margines i obramowanie, świetnie jest obliczyć rzeczywistą wysokość każdej linii:

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

Jeśli używasz rzadkiej czcionki o różnej wysokości każdej litery, to nie działa. Ale działa ze wszystkimi zwykłymi czcionkami, takimi jak Arial, mono, komiks, Verdana itp. Przetestuj swoją czcionkę.

Przykład:

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

Wynik: 6 Lines

Działa we wszystkich przeglądarkach bez rzadkich funkcji poza standardami.

Sprawdź: https://jsfiddle.net/gzceamtr/


Przede wszystkim bardzo dziękuję, właśnie tego szukałem. Czy mógłbyś wyjaśnić każdą linię funkcji obliczeniowej. Z góry dziękuję. SYA :)
LebCit

1
dla tych, którzy potrzebują odpowiedzi innej niż jQuery: clonecloneNode, hidestyle.visibility = "hidden", html('a<br>b')textContent='a\r\nb', appendTo('body')document.documentElement.appendChild, height()getBoundingClientRect().height
Eric

Ponieważ otrzymuję błąd 1px między wysokością wiersza a wysokością div, polecam tę odpowiedź. Połączenie tego i odpowiedzi @ Jeffa będzie jeszcze lepsze
Eric

6

Dla tych, którzy używają 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 jest mniej dokładny, ale zgodnie z tym może być lepiej obsługiwany przez różne przeglądarki
penkovsky

8
Kiedy jQuery zwraca „normalne” z $(selector).css('line-height');, nie otrzymasz liczby z tej funkcji.
bafromca,

5

Jestem przekonany, że teraz jest to niemożliwe. Tak było.

Implementacja getClientRects w IE7 zrobiła dokładnie to, czego chcę. Otwórz tę stronę w IE8, spróbuj ją odświeżyć, zmieniając szerokość okna i zobacz, jak odpowiednio zmienia się liczba wierszy w pierwszym elemencie. Oto kluczowe wiersze kodu JavaScript z tej strony:

var rects = elementList[i].getClientRects();
var p = document.createElement('p');
p.appendChild(document.createTextNode('\'' + elementList[i].tagName + '\' element has ' + rects.length + ' line(s).'));

Niestety dla mnie Firefox zawsze zwraca jeden prostokąt klienta na element, a IE8 robi to samo teraz. (Strona Martina Honnena działa dzisiaj, ponieważ IE renderuje ją w widoku zgodnym z IE; naciśnij F12 w IE8, aby grać w różnych trybach.)

To jest smutne. Wygląda na to, że po raz kolejny dosłowna, ale bezwartościowa implementacja specyfikacji Firefoksa wygrała z użyteczną wersją Microsoftu. A może brakuje mi sytuacji, w której nowy getClientRects może pomóc deweloperowi?


2
Jak wskazano w dokumentacji Mozilli element.getClientRects , przynajmniej dla elementów wbudowanych specyfikacja W3C daje w wyniku prostokąt dla każdego wiersza tekstu - nie jest to idealne rozwiązanie, ale przynajmniej coś.
natevw

getClientRects (). length jest poprawnym sposobem. getComputedStyle () może zwracać wartości takie jak „inherit”, „normal” i „auto”.
Binyamin

4

w oparciu o odpowiedź GuyPaddock z góry wydaje mi się, że to działa

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

sztuczka polega na tym, aby zwiększyć wysokość linii tak bardzo, że „połknie” wszelkie różnice w przeglądarce / systemie operacyjnym w sposobie renderowania czcionek

Sprawdziłem to z różnymi stylami i różnymi rozmiarami / rodzinami czcionek, jedyne, czego nie bierze pod uwagę (bo w moim przypadku nie miało to znaczenia), to dopełnienie - które można łatwo dodać do rozwiązania.


2

Nie, nie niezawodnie. Po prostu jest zbyt wiele nieznanych zmiennych

  1. Jaki system operacyjny (różne DPI, odmiany czcionek itp.)?
  2. Czy mają powiększony rozmiar czcionki, ponieważ są praktycznie niewidomi?
  3. Heck, w przeglądarkach webkit możesz faktycznie zmieniać rozmiar pól tekstowych według własnego uznania.

I tak dalej. Mam nadzieję, że pewnego dnia pojawi się taka metoda niezawodnego wykonania tego za pomocą JavaScript, ale do tego dnia masz pecha.

Nienawidzę tego typu odpowiedzi i mam nadzieję, że ktoś może mi udowodnić, że się mylę.


3
Jeśli policzysz linie po renderowaniu, wszystkie te niewiadome będą nieistotne. Twój punkt ma oczywiście zastosowanie, jeśli spróbujesz „oszacować”, ile wierszy używa przeglądarka podczas renderowania tekstu.
simon,

Powiedziałbym, że zmienną dotyczącą powiększania i / lub zmiany rozmiaru tekstu przez użytkowników można zignorować. Nigdy nie widziałem witryny, której projekt został zoptymalizowany pod kątem skalowania okna (po załadowaniu strony) lub zmiany rozmiaru tekstu pod kątem przeglądarki, więc nie sądzę, aby były to ważne czynniki w tym konkretnym pytaniu. Jednak zgadzam się, że nie ma wiarygodnych rozwiązań, są tylko „szacunki” oparte na pikselach, jak wskazywały inne osoby.
Kalko

2

Powinieneś być w stanie podzielić ('\ n'). Długość i uzyskać podziały linii.

aktualizacja: to działa na FF / Chrome, ale nie na 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
Wypróbuj i daj nam znać, jak to działa. Jestem śmiertelnie poważny, nie jestem sarkastyczny. Możesz użyć tego kodu w konsoli FireBuga na tej samej stronie. var the_text = $ ('. welovestackoverflow p'). text (); var numlines = the_text.split ("\ n"). length; alert (numlines);
KyleFarris

3
Dzięki za to zaskoczenie, nie wiedziałem, że to możliwe, ale nie tego chcę. Wydaje się, że Jquery liczy twarde podziały wierszy w źródle, a ja interesują mnie automatyczne podziały wierszy w wyniku. W przypadku elementu div „One Two Three” wynik powinien wynosić 1, ponieważ przeglądarka umieszcza cały tekst w jednym wierszu.
buti-oxa

Wtedy źle zrozumiałem twoje pytanie. Chcesz znaleźć obliczoną wysokość linii.
Chad Grant

1

getClientRectszwrócić prostokąty, klientów jak to i jeśli chcesz uzyskać linie, należy użyć funkcji follow jak to

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

Znalazłem sposób na obliczenie numeru linii, kiedy tworzyłem edytor HTML. Podstawowa metoda jest taka:

  1. W IE możesz wywołać getBoundingClientRects, zwraca on każdą linię jako prostokąt

  2. W webkicie lub nowym standardowym silniku html zwraca on prostokąty klienta każdego elementu lub węzła, w tym przypadku możesz porównać każdy prostokąt, to znaczy każdy musi być największy, więc możesz zignorować te prostokąty, których wysokość jest mniejsza (jeśli góra prostokąta jest mniejsza od niego, a dół jest większy od niego, warunek jest prawdziwy).

zobaczmy więc wynik testu:

wprowadź opis obrazu tutaj

Zielony prostokąt to największy prostokąt w każdym rzędzie

Granicą zaznaczenia jest czerwony prostokąt

Niebieski prostokąt jest granicą od początku do zaznaczenia po rozwinięciu, widzimy, że może być większy niż czerwony prostokąt, więc musimy sprawdzić dół każdego prostokąta, aby ograniczyć go do dołu czerwonego prostokąta.

        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

Wypróbuj to rozwiązanie:

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

Możesz zobaczyć to w akcji tutaj: https://jsfiddle.net/u0r6avnt/

Spróbuj zmienić rozmiar paneli na stronie (aby poszerzyć lub skrócić prawą stronę strony), a następnie uruchom go ponownie, aby zobaczyć, czy może wiarygodnie określić liczbę linii.

Ten problem jest trudniejszy, niż się wydaje, ale większość trudności pochodzi z dwóch źródeł:

  1. Renderowanie tekstu jest zbyt niskie w przeglądarkach, aby można było o niego bezpośrednio zapytać z JavaScript. Nawet pseudoselektor CSS :: first-line nie zachowuje się tak, jak inne selektory (nie można go odwrócić, na przykład, aby zastosować styl do wszystkich linii oprócz pierwszej).

  2. Kontekst odgrywa dużą rolę w obliczaniu liczby wierszy. Na przykład, jeśli wysokość linii nie została jawnie ustawiona w hierarchii elementu docelowego, możesz otrzymać „normalną” z powrotem jako wysokość linii. Ponadto element może być używany, box-sizing: border-boxa zatem podlegać wypełnieniu.

Moje podejście minimalizuje # 2 poprzez bezpośrednie przejęcie kontroli nad wysokością linii i uwzględnienie metody wymiarowania pudełka, co prowadzi do bardziej deterministycznego wyniku.


1

W niektórych przypadkach, na przykład w przypadku łącza obejmującego wiele wierszy w tekście nie wyjustowanym, można uzyskać liczbę wierszy i każdą współrzędną każdego wiersza, używając tego:

var rectCollection = object.getClientRects();

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

To działa, ponieważ każda linia byłaby inna, nawet tak nieznacznie. Dopóki są, są rysowane przez mechanizm renderujący jako inny „prostokąt”.


0

Zgodnie z sugestią @BobBrunius 2010 stworzyłem to za pomocą jQuery. Bez wątpienia można to poprawić, ale może niektórym pomóc.

$(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

Możesz porównać wysokość elementu i wysokość elementu za pomocą 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
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.