skip to Main Content

I can’t figure out how to get the element of a text node but I can get the parent element.

I’ve written a javascript routine to make a selection using a range. So I have a start node and end node which have been obtained with document.createTreeWalker(rootnode, NodeFilter.SHOW_TEXT, null, false).

I want to use scrollIntoView() but it seems I can only call this from an element. So at the moment I’m using startNode.parentElement.scrollIntoView.

let range = new Range()
range.setStart(startNode, startPos)
range.setEnd(endNode, endPos)
document.getSelection().removeAllRanges()
document.getSelection().addRange(range)
startNode.parentElement.scrollIntoView({ behavior: 'instant', block: 'start', inline: 'start' })

All good when the tags are small but if it were a large paragraph of text then the parent position could be a fair way off the actual elements position. Is there a way to get the element of a text node so I can call scrollIntoView?

As a work around I’m using range.getBoundingClientRect() and then setting the scrollTop of the page.

let bb=range.getBoundingClientRect()
pagetop.parentElement.scrollTop = bb.y+pagetop.parentElement.scrollTop-130 // 130 fudge value

It works :), but I’d rather use scrollIntoView or at least have a better understanding of what I’m doing wrong.

2

Answers


  1. To scroll to a text node in JavaScript, you can’t use scrollIntoView() directly on the text node. But, you can use a workaround by creating a temporary element.

    Like creating a <span> element right where you want, and scroll to that and then remove it from the DOM.

    let range = new Range();
    range.setStart(startNode, startPos);
    range.setEnd(endNode, endPos);
    
    let tempSpan = document.createElement('span');
    range.insertNode(tempSpan);
    
    tempSpan.scrollIntoView({ behavior: 'instant', block: 'start' });
    tempSpan.remove();
    
    document.getSelection().addRange(range);
    
    Login or Signup to reply.
  2. You can use the x and y coordinates of the DOMRect returned by the Range.prototype.getBoundingClientRect() method as arguments to window.scroll() like this:

    const nodeStart = new Text("nee");
    const nodeEnd = new Text("dle");
    
    function createHaystack() {
      const paragraph = document.querySelector("p.haystack");
    
      for (let i = 0; i < 100; i += 1) {
        paragraph.append(new Text("hay ".repeat(50)));
      }
    
      paragraph.append(nodeStart, nodeEnd, " ");
    
      for (let i = 0; i < 100; i += 1) {
        paragraph.append(new Text("hay ".repeat(100)));
      }
    }
    
    function findNeedle() {
      const range = new Range();
      range.setStart(nodeStart, 0);
      range.setEnd(nodeEnd, nodeEnd.length);
      document.getSelection().removeAllRanges();
      document.getSelection().addRange(range);
    
      const rangeRect = range.getBoundingClientRect();
      window.scroll(rangeRect.x, rangeRect.y);
    }
    
    createHaystack();
    document.querySelector("button").addEventListener("click", findNeedle);
    body { font-family: sans-serif; }
    <button>Find needle</button>
    <p class="haystack"></p>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search