How to calculate the number of lines in a text in a <p> or <div> with Javascript?

Asked

Viewed 229 times

1

My question is not restricted to the amount of line breaks <br> or with the use of \n in tags <pre>. What I want to know is how many lines a text displays in an HTML tag like <p> or <div>. Imagine I have the following body (body) HTML and script JS:

document.querySelector("input").addEventListener("click", function() {
  let quant = 3; /*Qual o comando a ser inserido aqui?*/
  let addS = "";
  if (quant > 1) {
    addS = "s";
  }
  alert("Há " + quant + " linha" + addS + " no texto!");
});
<input type=button value="Quantas linhas possui o texto abaixo?">
<p style="width: 300px">
  <!--Texto genérico abaixo -->
  Lorem ipsum porta consectetur sollicitudin arcu sodales elit nunc, morbi varius aliquam ultricies augue cras dui fringilla, auctor amet ac mattis praesent suspendisse nunc. arcu mi faucibus aliquam erat sagittis pharetra egestas, scelerisque nostra ligula
  aliquam rhoncus placerat nec, mattis at nullam morbi sapien magna. curae imperdiet senectus accumsan hendrerit pretium accumsan lorem curae fringilla, imperdiet sapien turpis curabitur dictum urna interdum leo inceptos tellus, fusce malesuada quisque
  venenatis platea neque euismod porta. fames ut donec hendrerit primis placerat integer amet sollicitudin interdum nisl, etiam posuere dictum dui semper netus orci arcu ad erat, vehicula nisl netus conubia arcu neque felis augue nec.
</p>

Already created the addEventListener in JS, the issue is the command that identifies the number of lines the text has in the element <p> (but not only the amount of tags <br> or of \n, and yes the breaks "natural").

  • It got a little confusing to what you refer to when you say "natural" line breaks. Do you say in relation to when the browser itself divides the text, for example to fit everything on the screen? Or does it refer to "enter" when someone types something? In the case of the Lorem ipsum example you put, it would have how many line breaks?

  • First answer: Yes, the "natural" breaks I quoted are the ones that the browser itself divides. Second answer: No. In the case of the text Lorem ipsum I put of example and considering the amount of "enter", it would have 0 line breaks.

1 answer

3


Calculation of number of lines

It is possible to calculate how many lines an element has by dividing its scrollHeight for his line-height and rounded up (if the result is 3.1 lines, actually the element needs to 4 lines to be displayed).

  • scrollHeight is a measure of the height of the element’s content, including content not visible on the screen due to the overflow.
  • line-height is a property that defines the height of a line box, i.e., the height of the line text including line spacing.

Algorithm for the calculation

To obtain the scrollHeight, just access the property directly from the HTML element elemento.scrollHeight.

To the line-height we have a little more complexity:

  1. line-height is a CSS value calculated by the browser. We use enntão window.getComputedStyle(elemento) to obtain all calculated style values of the element in question.

  2. As the above function returns a list of values of different properties, we search for the desired value with listaDeValoresCalculados.getPropertyValue('line-height').

  3. In the CSS, the line-height can hold values such as inherit, normal, 15px, 3rem etc. By obtaining the calculated value with the above function, we can receive or normal or the value in px.

  4. We convert the calculated value to integer with parseInt().

    4.1. If the value was 15px, now will be 15, all right.

    4.2. If the value was normal, now will be NaN. If it is NaN, we need to calculate the line-height otherwise.

    • 4.2.1. We clone the element with cloneNode() to have an element with all the same style properties.

    • 4.2.2. We take the height of the element with offSetHeight when he has a single line (<br>) and when it has two lines (<br><br>).

    • 4.2.3. Subtract the height of two lines by the height of one line (alturaDuasLinhas - alturaUmaLinha), that’s the line-height we will use. This is necessary as the height of a single line includes the padding, for example, it may be worth 40 when in fact the line-height is of 15px, and then the height of two lines would be worth 55, and 55 - 40 = 15.

Now we have the values of scrollHeight and line-height. We do the splitting and the bumping up: Math.ceil(scrollHeight / lineHeight).

Example

In the example below I apply the algorithm described above. Also, I decided to obtain the lineHeight only once, when the page loads with DOMContentLoaded, because it is a more complex process, I do it only once.

If your CSS changes dynamically, it may be line-height change, for example. In this case, adapt the code to your needs.

let lineHeightParagrafo;
document.addEventListener("DOMContentLoaded", function() {
  lineHeightParagrafo = getLineHeight(document.querySelector("p"))
});

document.querySelector("input").addEventListener("click", function() {
  const paragrafo = document.querySelector("p");
  const quant = Math.ceil(paragrafo.scrollHeight / lineHeightParagrafo);
  let addS = "";
  if (quant > 1) {
    addS = "s";
  }
  alert("Há " + quant + " linha" + addS + " no texto!");
});

function getLineHeight(elemento) {
  const estiloComputado = window.getComputedStyle(elemento);
  /* A propriedade pode ter valor 'normal' ou em 'px',
     mesmo que você tenha atribuído em 'rem' */
  const stringLineHeight = estiloComputado.getPropertyValue('line-height');
  let lineHeight = parseInt(stringLineHeight, 10);

  /* Caso o valor seja 'normal', então lineHeight não será um número,
     então criamos um elemento novo para calcular o lineHeight */
  if (isNaN(lineHeight)) {
    /* <br> fará com que o clone tenha apenas uma linha,
      para descobrir o lineHeight é necessário calcular a
      diferença de tamanho entre duas linhas, porque
      a existência de padding influenciará o retorno de offSetHeight */
    const clone = elemento.cloneNode();
    clone.innerHTML = '<br>';
    elemento.appendChild(clone);
    const alturaComUmaLinha = clone.offsetHeight;

    clone.innerHTML = '<br><br>';
    const alturaComDuasLinhas = clone.offsetHeight;
    elemento.removeChild(clone);

    lineHeight = alturaComDuasLinhas - alturaComUmaLinha;
  }

  return lineHeight;
}
<input type=button value="Quantas linhas possui o texto abaixo?">
<p style="width: 300px">
  <!--Texto genérico abaixo -->
  Lorem ipsum porta consectetur sollicitudin arcu sodales elit nunc, morbi varius aliquam ultricies augue cras dui fringilla, auctor amet ac mattis praesent suspendisse nunc. arcu mi faucibus aliquam erat sagittis pharetra egestas, scelerisque nostra ligula
  aliquam rhoncus placerat nec, mattis at nullam morbi sapien magna. curae imperdiet senectus accumsan hendrerit pretium accumsan lorem curae fringilla, imperdiet sapien turpis curabitur dictum urna interdum leo inceptos tellus, fusce malesuada quisque
  venenatis platea neque euismod porta. fames ut donec hendrerit primis placerat integer amet sollicitudin interdum nisl, etiam posuere dictum dui semper netus orci arcu ad erat, vehicula nisl netus conubia arcu neque felis augue neca.
</p>


The algorithm to obtain the line-height in the event of normal was adapted of a reply from Soen.

  • 1

    Very good, I put width: 100% and when resizing the browser replied straight!

  • Thanks! I use a similar code to create a <textarea> that increases the number of lines as you type :)

  • Thank you very much for your reply, Raphael. I even thought about applying a model based on dividing the height of the element by the height of the line in the code, but I was in doubt about the existence of an existing command in the JS language that counts lines (by the way, that’s right hehe). Vlw!

Browser other questions tagged

You are not signed in. Login or sign up in order to post.