Search code examples
javascriptrangegetselection

Ignore span stag in window selection to get the start and end index


I have a html tag which is

<span>This is first text<span class="ignore">Second</span> This is third text<span>

I am trying to get the start and end index from the selected text. When I select third I get start and end index as 34 39 But I expect 27 32

I tried the below approach

export const findTextRange = (element) => {
  if (!element) return;
  let start = 0, end = 0;
  let sel, range, priorRange, text;
  if (typeof window.getSelection != "undefined") {
    sel = window.getSelection();
    text = sel + '';
    if (window.getSelection().rangeCount <= 0) {
      return;
    }
    range = window.getSelection().getRangeAt(0);
    priorRange = range.cloneRange();
    priorRange.selectNodeContents(element);
    priorRange.setEnd(range.startContainer, range.startOffset);
    start = priorRange.toString().length;
    end = start + (sel + '').length;
  } else if (typeof document.selection !== "undefined" &&
    (sel = document.selection).type !== "Control") {
    text = sel + '';
    range = sel.createRange();
    priorRange = document.body.createTextRange();
    priorRange.moveToElementText(element);
    priorRange.setEndPoint("EndToStart", range);
    start = priorRange.text.length;
    end = start + (sel + '').length;
  }
  return { start, end, text };
}

Is there any way where I can ignore the span element with ignore class.


Solution

  • Store the initial HTML, then remove all elements having the .ignore class:

    const html = element.innerHTML;
    element.querySelectorAll('.ignore').forEach((e) => e.remove());
    

    After getting the range, restore the original HTML:

    element.innerHTML = html;
    

    Snippet

    const findTextRange = (element) => {
      if (!element) return;
      const html = element.innerHTML; // store original HTML
      element.querySelectorAll('.ignore').forEach((e) => e.remove()); // remove ignore elements
      
      let start = 0, end = 0;
      let sel, range, priorRange, text;
      if (typeof window.getSelection != "undefined") {
        sel = window.getSelection();
        text = sel + '';
        if (window.getSelection().rangeCount <= 0) {
          return;
        }
        range = window.getSelection().getRangeAt(0);
        priorRange = range.cloneRange();
        priorRange.selectNodeContents(element);
        priorRange.setEnd(range.startContainer, range.startOffset);
        start = priorRange.toString().length;
        end = start + (sel + '').length;
      } else if (typeof document.selection !== "undefined" &&
        (sel = document.selection).type !== "Control") {
        text = sel + '';
        range = sel.createRange();
        priorRange = document.body.createTextRange();
        priorRange.moveToElementText(element);
        priorRange.setEndPoint("EndToStart", range);
        start = priorRange.text.length;
        end = start + (sel + '').length;
      }
      element.innerHTML = html;  // restore HTML
      console.log(start, end, text);
      return { start, end, text };
    }
    
    document.querySelector('#P').addEventListener('click', function() {findTextRange(this)});
    <span id="P">This is first text<span class="ignore">Second</span> This is third text<span>