Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to split an HTML paragraph up into its lines of text with JavaScript

Is it possible (and how can I) split a paragraph of text up into its respective lines with JavaScript. What I'd like to do is implement hanging punctuation on every line of a paragraph or blockquote, not just the starting line.

Any other ideas would be welcome too!

Clarification I was asked to be less ambiguous about what "split a paragraph into its respective lines" means.

In HTML a <p> element creates a block of text. Similarly, so do many other elements. These bodies of text get wrapped to fit the width (whether that width is set by the css or assumed by the default setting) I can't seem to detect where the line breaks happen using regex or any other means. So let's say a paragraph ends up being 7 lines long, I'd like to be able to detect that it's seven lines, and where those lines start and end.

Looking for a \n or a \r doesn't seem to yield anything.

like image 265
Costa Michailidis Avatar asked Jan 13 '15 05:01

Costa Michailidis


2 Answers

A brute force way to do it is to split all the words in your paragraph and make spans of them. You can then measure the offsetTop property of your spans to find which ones end up in different lines.

In the snippet below, getLines() returns an array of arrays where each inner array is an contains the span elements for each word in a line. You could then manipulate that as you wish to create your hanging punctuation using some CSS, maybe by inserting absolutely positioned spans with your punctuation.

//So it runs after the animation
setTimeout(function(){
    splitLines();
    showLines();
}, 1000)

function showLines() {
  var lines = getLines();
  console.log(
    lines.map(function(line) {
      return line.map(function(span) {
        return span.innerText;
      }).join(' ')
    }));
}

function splitLines() {
  var p = document.getElementsByTagName('p')[0];
  p.innerHTML = p.innerText.split(/\s/).map(function(word) {
    return '<span>' + word + '</span>'
  }).join(' ');
}



function getLines() {
  var lines = [];
  var line;
  var p = document.getElementsByTagName('p')[0];
  var words = p.getElementsByTagName('span');
  var lastTop;
  for (var i = 0; i < words.length; i++) {
    var word = words[i];
    if (word.offsetTop != lastTop) {
      lastTop = word.offsetTop;
      line = [];
      lines.push(line);
    }
    line.push(word);
  }
  return lines;
}
<p>Here is a paragraph that we want to track lines for. Here is a paragraph that we want to track lines for. Here is a paragraph that we want to track lines for Here is a paragraph that we want to track lines for Here is a paragraph that we want to track
  lines for Here is a paragraph that we want to track lines for</p>

Here's a fiddle that you can resize the window so the paragraph changes size http://jsfiddle.net/4zs71pcd/1/

like image 164
Juan Mendes Avatar answered Sep 20 '22 23:09

Juan Mendes


Looks like the hanging-punctuation css property only makes sure that any punctuation at the start of the first formatted line of an element hangs. So you would want to dynamically split the text into lines of the correct length, throw those into new <p> elements (or blockquotes) & apply hanging-punctuation: 'first' to those new elements. As of right now no major browser supports the hanging-punctuation property (citation).

Normally I would recommend checking where the newline character (\n) is inside the text, but most often no one explicitly puts that in the text they write. Instead they let the browser decide where to add the new lines depending on the window size (something like word wrap).This gets even trickier when you start to consider that there could be multiple lines in a given <p> element, and depending on the size of the browser window, the line could be split anywhere. You'd have to grab the text, find the width of it's container, and somehow see where in the text string it hits that width. Heres a great blogpost that talks about how to implement this in a more general sense though.

like image 37
Keenan Lidral-Porter Avatar answered Sep 20 '22 23:09

Keenan Lidral-Porter