Abreham
Abreham

Reputation: 445

Search text in a particular element in the HTML and return the Range object for it

I want to search for a text in HTML element and return the Range object for the text. I wanted to use the range to do some manipulation like highlighting and replacing the text. Similar to Grammarly's error highlighting feature.

What I have tried so far:

  1. Loop through the Element's childer to look for the text and construct the Range. But this approach has 2 issues.

a. it's very slow (350ms) and
b.fails to find some texts

const getRangeofText = (text:string) => {
  const children = document.activeElement;
  let finalNode;
  let parent = children;
  let i = 12;
  let isFound = false;
  while (parent.children.length > 0 && i > 0) {
    i--;
    console.log("I: ", i);
    console.log("traversing through: ", parent.childNodes);
    for (const item of Array.from(parent.childNodes)) {
      console.log("item: ", item);
      if (item.nodeType === 3 && item.textContent.includes(text)) {
        console.log("found in TEXT NODE");
        finalNode = item;
        isFound = true;
      }
      if (item.nodeType === 1 && item.textContent.includes(text)) {
        console.log("found in ELEMENT NODE");
        parent = item as Element;
        finalNode = item;
        isFound = true;
      }
    }
  }

  console.log("final node: ", finalNode);
  // console.log("node type: ", finalNode.nodeType);

  const range = new Range();
  let startNode;
  let endNode;
  let arr = text.split(" ");
  let startWord = arr[0];
  let endWord = arr[arr.length - 1];
  if (finalNode) {
    if (finalNode.nodeType === 1) {
      let childNodes = finalNode.childNodes;
      for (const item of Array.from(childNodes)) {
        if (item.textContent.includes(startWord)) {
          startNode = item;
        }
        if (item.textContent.includes(endWord)) {
          endNode = item;
        }
      }
     

      range.setStart(startNode, 0);
      range.setEndAfter(endNode);
    } else {
      range.setStart(finalNode, 0);
      range.setEnd(finalNode, text.length);
    }

    window.getSelection().addRange(range);
  }
};

Here is the Codesandbox for the above code.

  1. Using Rangy's findText() method. And this approach is very slow too (200ms)

What other options do I have? Or how can optimize these functions?

Upvotes: 1

Views: 453

Answers (1)

Abreham
Abreham

Reputation: 445

function getRanges(a) {
  let ranges = [];
  const scrollTop = document.documentElement.scrollTop;
  while (window.find(a)) {
    var rng = window.getSelection().getRangeAt(0);
    ranges = [...ranges, rng];
    document.documentElement.scrollTop = scrollTop;
  }
  return ranges;
}

const getRangeElements = (str) => {
  window.getSelection().removeAllRanges();
  const ranges = getRanges(str);
  window.getSelection().removeAllRanges();
  return ranges;
};

getRangeElements("Text to search for")

This works but it blurs out the page and whatever element (eg. text input) that was focus will get blurred. Any way we can avoid the blur?

Upvotes: 0

Related Questions