Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Given an offset in an extracted combined text string, how to determine containing node and offset?

Given the following HTML (just an example):

<div id="container"><span>This is <strong>SOME</strong> great example, <span style="color: #f00">Fred!</span></span></div>

One can extract the text using e.g. jQuery's text() function:

var text = $('container').text();

Now, what would be the simplest, fastest, most elegant way to determine that the offset 10 in the extracted text corresponds to the offset 2 of the text node inside the <strong>SOME</strong> node in the example above? Also, how would one do the inverse, i.e. determining the offset 10in the extracted text from the <strong>DOM object and the offset 2?

like image 741
Håvard S Avatar asked Oct 19 '25 11:10

Håvard S


1 Answers

You can use TreeWalker to get a pretty elegant solution:

/**
 * @param {Element} element
 * @param {number} absoluteOffset
 * @returns {[textNode: Text, relativeOffset: number] | null}
 */
function getRelativeOffsetWithinChildTextNode(element, absoluteOffset) {
  let offset = absoluteOffset
  const walker = element.ownerDocument.createTreeWalker(element, NodeFilter.SHOW_TEXT)
  while (walker.nextNode()) {
    const { currentNode } = walker
    const text = currentNode.nodeValue
    if (text.length >= offset) {
      return [currentNode, offset]
    }
    offset -= text.length
  }
  return null
}

// usage
const parent = document.getElementById('container')
const absoluteOffsets = [0, 8, 10, 12, 20, 30, 999]

for (const absoluteOffset of absoluteOffsets) {
  const result = getRelativeOffsetWithinChildTextNode(parent, absoluteOffset)
  
  if (result == null) {
    console.log(`Absolute offset ${absoluteOffset} is out of bounds (no text node at this offset)`)
  } else {
    const [textNode, relativeOffset] = result
    const { parentElement } = textNode
    const childNodes = [...parentElement.childNodes].filter((node) => node instanceof Text)
    const num = childNodes.indexOf(textNode) + 1
    console.log(`Absolute offset ${absoluteOffset} => relative offset ${relativeOffset} in ${parentElement.tagName}'s text node #${num}`)
  }
}
<div id="container">This is <strong>SOME</strong> great example, <em>Fred!</em></div>
like image 128
Hemlock Avatar answered Oct 21 '25 00:10

Hemlock



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!