10

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.

Costa Michailidis
  • 7,691
  • 15
  • 72
  • 124
  • could you explain what you mean by hanging punctuation? is this the same as multiline ellipses? – haxxxton Jan 13 '15 at 05:39
  • It's when punctuation at the beginning or end of a line hangs outside the left or right margins: http://css-tricks.com/almanac/properties/h/hanging-punctuation/ – Costa Michailidis Jan 13 '15 at 05:48
  • The question title does not match the question body, where the essential question is “hanging punctuation” (a vague word, but surely different from line division). Neither of them explains what it would mean to split a paragraph into its lines (a paragraph may be formatted into lines in infinitely many lines). You should unambiguously explain what you want, in the question itself, and probably illustrate it wirh an image. – Jukka K. Korpela Jan 13 '15 at 09:33
  • Do you need to know exactly where it splits, or is just the number of lines OK? For the number of lines, you can just divide the paragraph's height by its line-height http://stackoverflow.com/questions/9063178/check-how-many-lines-will-a-p-tag-occupy To get exact text positions, you would have to break a paragraph into ``s and get the position of each `` – Ruan Mendes Jan 13 '15 at 14:29
  • Ah, that's really smart. The number of lines might be helpful, but ultimately it's about knowing if punctuation occurs at the beginning or end of a line. – Costa Michailidis Jan 13 '15 at 20:50

2 Answers2

11

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/

Ruan Mendes
  • 90,375
  • 31
  • 153
  • 217
  • This is really helpful! Thanks so much. Maybe you could even split up the paragraph on all relevant punctuation marks, optionally adding negative padding or something to shift them. But how to tell if the punctuation falls at the beginning or end of a line? – Costa Michailidis Jan 13 '15 at 20:49
  • @Costa You can tell if it's at the beginning or at the end of a line if it's the first or last item in each of the sub-arrays returned by `getLines()` – Ruan Mendes Jan 13 '15 at 21:05
1

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.