Skip to content
Advertisement

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.

Advertisement

Answer

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>
Advertisement