Come posso contare le righe di testo all'interno di un elemento DOM? Posso?


127

Mi chiedo se ci sia un modo per contare le righe all'interno di un div, ad esempio. Supponiamo di avere un div in questo modo:

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

A seconda di molti fattori, il div può avere una, due o anche quattro righe di testo. C'è un modo per far sapere al copione?

In altre parole, le interruzioni automatiche sono rappresentate in DOM?

Risposte:


89

Se la dimensione del div dipende dal contenuto (che presumo sia il caso dalla tua descrizione), puoi recuperare l'altezza del div usando:

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

E dividi per l'altezza della linea del carattere:

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

O per ottenere l'altezza della linea se non è stata impostata esplicitamente:

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

Dovrai anche prendere in considerazione il riempimento e la spaziatura tra le linee.

MODIFICARE

Test completamente autonomo, impostazione esplicita dell'altezza della riga:

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
È un buon inizio tranne per il fatto che l'altezza della linea potrebbe non essere sempre stata impostata. Dovresti ottenerlo dallo stile calcolato dell'elemento.
Chetan S

Ripensandoci, l'altezza della riga non deve essere sempre numerica (o in pixel). Quindi, se il tuo codice si basa su di esso e la tua convenzione CSS lo consente, dovresti probabilmente impostare un'altezza di riga in pixel.
Chetan S

6
@Chetan - l'impostazione delle dimensioni del testo in pixel è generalmente considerata una cosa negativa. astahost.com/Sizes-Webdesign-Em-Vs-Px-t8926.html
annakata

6
Questo può funzionare solo nel caso più semplice (come il mio esempio). Se ci sono intervalli all'interno, elementi inline-block e così via, la divisione diretta per la dimensione del carattere (genitore) è inutile. Comunque è meglio di niente, grazie.
buti-oxa

2
Penso che un metodo migliore per ottenere un calcolo line-height(che non è impostato esplicitamente) sarebbe quello di utilizzarewindow.getComputedStyle(element, null).getPropertyValue('line-height')
rnevius

20

Una soluzione è racchiudere ogni parola in un tag span usando lo script. Quindi, se la dimensione Y di un dato tag span è inferiore a quella del suo predecessore immediato, si è verificata un'interruzione di riga.


Intelligente! Come si fa a farlo? Immagino che tu dia per scontato il paragrafo solo testo (come ho nell'esempio). Non avevo in mente questa limitazione, ma potrebbe essere OK, a condizione che lo script non si blocchi quando c'è qualcos'altro all'interno del paragrafo.
buti-oxa

1
A pensarci bene, questo metodo fallisce se ci sono intervalli allineati verticalmente nella linea. Quindi, anche il paragrafo di solo testo può essere conteggiato in modo errato.
buti-oxa

Il mio div ha testo e immagini ... fortunatamente le immagini sono posizionate in modo assoluto quindi penso che sarebbero escluse. : D Comunque ho usato lo stesso codice che mi hai suggerito ma con un div non un span, grazie +1!
Albert Renshaw

20

Controlla la funzione getClientRects () che può essere utilizzata per contare il numero di righe in un elemento. Ecco un esempio di come usarlo.

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

Restituisce un oggetto DOM javascript. La quantità di righe può essere conosciuta in questo modo:

var amount_of_lines = message_lines.length;

Può restituire l'altezza di ogni riga e altro ancora. Visualizza l'intera gamma di cose che può fare aggiungendolo al tuo script, quindi guardando nel registro della console.

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

Anche se alcune cose da notare è che funziona solo se l'elemento contenitore è inline, tuttavia puoi circondare l'elemento inline contenitore con un elemento block per controllare la larghezza in questo modo:

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

Anche se non consiglio di codificare lo stile in questo modo. È solo per scopi di esempio.


3
Questa e l'altra risposta basata su getClientRects sono di gran lunga migliori di quella accettata
matteo

2
Questa dovrebbe essere la risposta accettata. Grazie @ Digital-Christie
Antuan

12

Non ero soddisfatto delle risposte qui e su altre domande. La risposta con il punteggio più alto non prende paddingo tiene borderconto e quindi ovviamente ignorabox-sizing anche. La mia risposta combina alcune tecniche qui e su altri thread per ottenere una soluzione che funzioni in modo soddisfacente.

Non è perfetto: quando non è stato possibile recuperare alcun valore numerico per line-height(ad esempio normalo inherit), utilizza semplicemente il font-sizemoltiplicato per 1.2. Forse qualcun altro può suggerire un modo affidabile per rilevare il valore dei pixel in quei casi.

Oltre a questo, è stato in grado di gestire correttamente la maggior parte degli stili e dei casi che ho lanciato.

jsFiddle per giocare e testare. Anche in linea di seguito.

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>


post utile ... Grazie
Sinto

Buona considerazione, ma nel mio caso l'altezza di una riga div è superiore all'altezza della riga di 1px, senza alcun riempimento e bordo. Quindi ottenere l'altezza della linea pettinando la tua risposta e la risposta di @ e-info128 (metodo cloneNode) potrebbe essere una scelta migliore
Eric

8

Clona l'oggetto contenitore e scrivi 2 lettere e calcola l'altezza. Questo restituisce l'altezza reale con tutto lo stile applicato, l'altezza della linea, ecc. Ora, calcola l'altezza dell'oggetto / la dimensione di una lettera. In Jquery, l'altezza eccelle il padding, il margine e il bordo, è ottimo calcolare l'altezza reale di ogni linea:

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

Se utilizzi un carattere raro con un'altezza diversa di ogni lettera, non funziona. Ma funziona con tutti i caratteri normali, come Arial, mono, fumetti, Verdana, ecc. Prova con il tuo carattere.

Esempio:

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

Risultato: 6 Lines

Funziona con tutti i browser senza funzioni rare fuori standard.

Controlla: https://jsfiddle.net/gzceamtr/


Prima di tutto, voglio dire grazie mille, proprio quello che stavo cercando. Potresti spiegare ogni riga della funzione di calcolo. Grazie mille in anticipo. SYA :)
LebCit

1
per coloro che necessitano di una risposta non jQuery: clonecloneNode, hidestyle.visibility = "hidden", html('a<br>b')textContent='a\r\nb', appendTo('body')document.documentElement.appendChild, height()getBoundingClientRect().height
Eric

Poiché ricevo un errore di 1px tra l'altezza della riga e l'altezza del div, consiglio questa risposta. Combinando questo e la risposta di @ Jeff sarà ancora migliore
Eric

6

Per coloro che usano 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 è meno preciso, ma secondo questo potrebbe essere supportato meglio da vari browser
penkovsky

8
Quando jQuery restituisce "normale" da $(selector).css('line-height');, non otterrai un numero da quella funzione.
ba dal

5

Sono convinto che ora sia impossibile. Lo era, però.

L'implementazione di getClientRects di IE7 ha fatto esattamente quello che volevo. Apri questa pagina in IE8, prova ad aggiornarla variando la larghezza della finestra e guarda come cambia il numero di righe nel primo elemento di conseguenza. Ecco le linee chiave del javascript da quella pagina:

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

Sfortunatamente per me, Firefox restituisce sempre un rettangolo client per elemento e IE8 fa lo stesso ora. (La pagina di Martin Honnen funziona oggi perché IE la esegue il rendering nella vista IE compat; premere F12 in IE8 per giocare con modalità diverse.)

Questo è triste. Sembra che ancora una volta l'implementazione letterale ma inutile di Firefox delle specifiche abbia vinto su quella utile di Microsoft. O mi manca una situazione in cui il nuovo getClientRects può aiutare uno sviluppatore?


2
Come la punta di Mozilla da element.getClientRects documentazione, almeno per quanto riguarda in linea gli elementi della specifica W3C fa seguito a un rettangolo per ogni riga di testo - non è l'ideale, ma almeno qualcosa.
natevw

getClientRects (). length è il modo corretto. getComputedStyle () può restituire valori come "inherit", "normal" e "auto".
Binyamin

4

sulla base della risposta di GuyPaddock dall'alto, questo sembra funzionare per me

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

il trucco qui sta nell'aumentare così tanto l'altezza della riga da "ingoiare" qualsiasi differenza tra browser / sistema operativo nel modo in cui visualizzano i caratteri

Controllato con vari stili e diverse dimensioni / famiglie di caratteri, l'unica cosa che non tiene in considerazione (poiché nel mio caso non importava), è il riempimento, che può essere facilmente aggiunto alla soluzione.


2

No, non affidabile. Ci sono semplicemente troppe variabili sconosciute

  1. Quale sistema operativo (DPI diversi, variazioni dei caratteri, ecc ...)?
  2. Hanno la dimensione del carattere ridimensionata perché sono praticamente ciechi?
  3. Diamine, nei browser webkit, puoi effettivamente ridimensionare le caselle di testo secondo i desideri del tuo cuore.

L'elenco potrebbe continuare. Un giorno spero che ci sarà un metodo del genere per ottenere questo risultato in modo affidabile con JavaScript, ma fino a quel giorno non sarai fortunato.

Odio questo tipo di risposte e spero che qualcuno possa dimostrarmi che mi sbaglio.


3
Se conti le righe dopo il rendering, tutte quelle incognite sono irrilevanti. Il tuo punto vale, ovviamente, se provi a "stimare" quante righe usa il browser per il rendering del testo.
simon

Direi che la variabile degli utenti che ingrandiscono e / o modificano la dimensione del testo può essere ignorata. Non ho mai visto un sito con un design ottimizzato per adattarsi al ridimensionamento della finestra (dopo il caricamento della pagina) o alla modifica della dimensione del testo dal browser, quindi non credo che questi siano fattori importanti in questa domanda specifica. Tuttavia sono d'accordo che non ci sono soluzioni affidabili, solo le "stime" basate sui pixel come hanno sottolineato altre persone.
Kalko

2

Dovresti essere in grado di dividere ('\ n'). Length e ottenere le interruzioni di riga.

aggiornamento: funziona su FF / Chrome ma non su 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
Provalo e facci sapere come va. Sono mortalmente serio, non essendo sarcastico. Puoi usare questo codice nella console di FireBug in questa stessa pagina. var the_text = $ ('. welovestackoverflow p'). text (); var numlines = the_text.split ("\ n"). length; allarme (numLines);
KyleFarris

3
Grazie per questa sorpresa, non sapevo fosse possibile, ma non è quello che voglio. Jquery sembra contare le interruzioni di riga rigide nella sorgente e sono interessato alle interruzioni di riga automatiche nel risultato. Nel caso del div "One Two Three", il risultato dovrebbe essere 1, perché il browser mette tutto il testo in una riga.
buti-oxa

Poi ho frainteso la tua domanda. Allora vuoi trovare l'altezza della linea calcolata.
Chad Grant,

1

getClientRectsrestituisci il client rects in questo modo e se vuoi ottenere le linee, usa la funzione follow come questa

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

Ho trovato un modo per calcolare il numero di riga quando sviluppo un editor html. Il metodo principale è quello:

  1. In IE puoi chiamare getBoundingClientRects, restituisce ogni riga come un rettangolo

  2. Nel webkit o nel nuovo motore html standard, restituisce ogni elemento o i rettangoli client del nodo, in questo caso puoi confrontare ogni rettangolo, voglio dire che ogni rettangolo deve essere il più grande, quindi puoi ignorare quei rettangoli che l'altezza è più piccola (se c'è un rettangolo in alto più piccolo di esso e in basso più grande di esso, la condizione è vera.)

quindi vediamo il risultato del test:

inserisci qui la descrizione dell'immagine

Il rettangolo verde è il rettangolo più grande di ogni riga

Il rettangolo rosso è il confine di selezione

Il rettangolo blu è il confine dall'inizio alla selezione dopo l'espansione, vediamo che può essere più grande del rettangolo rosso, quindi dobbiamo controllare il fondo di ogni rettangolo per limitarlo deve essere più piccolo del fondo del rettangolo rosso.

        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

Prova questa soluzione:

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

Puoi vederlo in azione qui: https://jsfiddle.net/u0r6avnt/

Prova a ridimensionare i pannelli sulla pagina (per allargare o accorciare il lato destro della pagina) e poi eseguirlo di nuovo per vedere che può dire in modo affidabile quante righe ci sono.

Questo problema è più difficile di quanto sembri, ma la maggior parte delle difficoltà deriva da due fonti:

  1. Il rendering del testo è di livello troppo basso nei browser per essere interrogato direttamente da JavaScript. Anche lo pseudo-selettore CSS :: first-line non si comporta esattamente come fanno gli altri selettori (non è possibile invertirlo, ad esempio, per applicare lo stile a tutte tranne la prima riga).

  2. Il contesto gioca un ruolo importante nel modo in cui calcoli il numero di linee. Ad esempio, se l'altezza della linea non è stata impostata esplicitamente nella gerarchia dell'elemento di destinazione, potresti tornare "normale" come altezza della linea. Inoltre, l'elemento potrebbe essere in uso box-sizing: border-boxe quindi essere soggetto a riempimento.

Il mio approccio minimizza il n. 2 prendendo direttamente il controllo dell'altezza della linea e prendendo in considerazione il metodo di dimensionamento della scatola, portando a un risultato più deterministico.


1

In alcuni casi, come un collegamento che si estende su più righe in un testo non giustificato, puoi ottenere il conteggio delle righe e ogni coordinata di ogni riga, quando usi questo:

var rectCollection = object.getClientRects();

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

Questo funziona perché ogni riga sarebbe diversa anche se leggermente. Finché sono, vengono disegnati come un "rettangolo" diverso dal renderizzatore.


0

Seguendo il suggerimento di @BobBrunius 2010, l'ho creato con jQuery. Senza dubbio potrebbe essere migliorato, ma potrebbe aiutare alcuni.

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

Puoi confrontare l'altezza dell'elemento e l'altezza dell'elemento con 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
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.