Get a specific line from a (line-break: normal) html

Viewed 91

I want to know if this is possible using JavaScript. I want to extract, say third line, from the following page:

<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum felis elit, scelerisque id rhoncus et, hendrerit nec turpis. Phasellus eget condimentum justo. Aliquam porta, risus sed elementum hendrerit, turpis urna posuere libero, eget facilisis sem purus sed mi. Nulla pulvinar nibh quis bibendum lacinia. Aenean eu nibh pharetra, imperdiet mi eget, vehicula mauris. In hac habitasse platea dictumst. Phasellus ante enim, bibendum quis turpis a, volutpat auctor mi. Mauris scelerisque sem a ornare dignissim. Nullam in sem ac turpis aliquet dictum sit amet dignissim est.</p>

I believe my html looks like:

enter image description here

And the red underlined line is the third line, which is adjusted dynamically in the view.

I tried .innerHTML and .innerText method, but it fails to identify the line breaks.

I thought if I can somehow get the scrollHeight of each lines and then use JavaScript, but I didn't found any supporting methods on internet. Any ideas?

Note: One can assume that he/she has access to the p element. We may proceed with a client-side JS/HTML solution.

1 Answers

We can do the following:

  • Split the text of the paragraph by the space character to get all the words
  • Set the paragraph's text to an empty string
  • Append each word and measure the content size of the paragraph (use this sparingly, measuring this way causes a lot of layout recalculation)
  • When the content size of the paragraph is not the same as the previous content size, we know that a line-break has happened
  • When we are at the nth line, add the appended words until a new line is reached
  • Said variable is the extracted string

This solution does not block UI events even when the text is very long. To achieve that in this solution, we can split the long-running task to smaller ones so that UI events can still be handled. To split it, we can use setTimeout(..., 0). Using setTimeout this way defers the shorter-running tasks. They only run if they are the most front of the message queue. Only when a shorter-running task is finished will the next shorter-running task be deferred to the end of the message queue. This allows for other UI-related messages (e.g. the callback function for your click event listener) to be run also; thus nonblocking.

const p = document.querySelector('p')
const texts = p.innerText.split(' ')
const textsLength = texts.length
const nthLine = 3 // The line from which you extract the string

let i = 0
let currentWord = 0
let currentLine = 0
let currentBoxHeight = 0
let nthLineText = ''

p.innerText = ''
function getNthLineText() {
  // Split the long-running tasks of measuring words to only 100 word per callback function
  for (currentWord; currentWord < (i + 1) * 100 && currentWord < textsLength; currentWord++) {
    p.innerText += ` ${texts[currentWord]}`
    
    // Paragraph box is larger? Line-break occurred
    if (p.scrollHeight > currentBoxHeight) {
      currentBoxHeight = p.scrollHeight
      currentLine += 1
    }
    
    // We're at the nth line we want to extract, add the appended word to the result variable
    if (currentLine === nthLine) {
      nthLineText += ` ${texts[currentWord]}`
    }
  }
  
  if (currentLine > nthLine || currentWord >= textsLength) {
    console.log(`String extracted: ${nthLineText === '' ? 'None extracted' : nthLineText}`)
    p.innerText = texts.join(' ')
  } else {
    setTimeout(getNthLineText, 0)
  }
}

getNthLineText()
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum felis elit, scelerisque id rhoncus et, hendrerit nec turpis. Phasellus eget condimentum justo. Aliquam porta, risus sed elementum hendrerit, turpis urna posuere libero, eget facilisis sem purus sed mi. Nulla pulvinar nibh quis bibendum lacinia. Aenean eu nibh pharetra, imperdiet mi eget, vehicula mauris. In hac habitasse platea dictumst. Phasellus ante enim, bibendum quis turpis a, volutpat auctor mi. Mauris scelerisque sem a ornare dignissim. Nullam in sem ac turpis aliquet dictum sit amet dignissim est.</p>

Related