0

I am writing a mini code to editor in the browser. Task: when entering such words as if, else, for ... they should be highlighted.

textarea.addEventListener("input", function() {
  // Get the text inside the textarea
  const text = this.textContent;

  // Loop through each keyword and highlight it in the text
  for (let keyword of keywords) {
    const pattern = new RegExp("\\b" + keyword + "\\b", "g");
    const replacement = "<span class='highlight'>" + keyword + "</span>";
    const highlightedText = text.replace(pattern, replacement);
    this.innerHTML = highlightedText;
  }
});
<div name="code-space" id="code__space" contenteditable="true"></div>

Maybe I need to rework the idea. I will be glad to receive recommendations.

Quentin
  • 914,110
  • 126
  • 1,211
  • 1,335
Nazar
  • 27
  • 4
  • 2
    The ` – Mr. Polywhirl Feb 27 '23 at 16:02
  • 1
    ` – Pointy Feb 27 '23 at 16:03
  • 1
    textarea is for plain text, you need to use an editable div I think: `
    `
    – Scott Weaver Feb 27 '23 at 16:03
  • ok, i changed
    – Nazar Feb 27 '23 at 16:03
  • 2
    You haven't change the rest of your code to account for the new `div` and you haven't included the code that defines `keywords` and your code still references a `textarea` that now doesn't exist. – Scott Marcus Feb 27 '23 at 16:23

1 Answers1

0

Is this what you want? You are using a contenteditable div in your HTML, not a textarea, so I added my updates on the input event of the editable text:

var editor = document.querySelector('#code__space');

const keywords = ['if', 'else', 'for'];
let timeoutId;

editor.addEventListener('input', function() {
  clearTimeout(timeoutId);
  timeoutId = setTimeout(function() {
    var text = editor.textContent;

    for (let keyword of keywords) {
      const pattern = new RegExp('\\b' + keyword + '\\b', 'g');
      const replacement = '<span class="highlight">' + keyword + '</span>';
      text = text.replace(pattern, replacement);
    }
    
    const sel = window.getSelection();
    const node = sel.focusNode;
    const offset = sel.focusOffset;
    const pos = getCursorPosition(editor, node, offset, { pos: 0, done: false });
    if (offset === 0) pos.pos += 0.5;

    editor.innerHTML = text;
    
    // restore the position
    sel.removeAllRanges();
    const range = setCursorPosition(editor, document.createRange(), {
      pos: pos.pos,
      done: false,
    });
    range.collapse(true);
    sel.addRange(range);
    
  }, 1000);
});

// get the cursor position from .editor start
function getCursorPosition(parent, node, offset, stat) {
  if (stat.done) return stat;

  let currentNode = null;
  if (parent.childNodes.length == 0) {
    stat.pos += parent.textContent.length;
  } else {
    for (let i = 0; i < parent.childNodes.length && !stat.done; i++) {
      currentNode = parent.childNodes[i];
      if (currentNode === node) {
        stat.pos += offset;
        stat.done = true;
        return stat;
      } else getCursorPosition(currentNode, node, offset, stat);
    }
  }
  return stat;
}

//find the child node and relative position and set it on range
function setCursorPosition(parent, range, stat) {
  if (stat.done) return range;

  if (parent.childNodes.length == 0) {
    if (parent.textContent.length >= stat.pos) {
      range.setStart(parent, stat.pos);
      stat.done = true;
    } else {
      stat.pos = stat.pos - parent.textContent.length;
    }
  } else {
    for (let i = 0; i < parent.childNodes.length && !stat.done; i++) {
      currentNode = parent.childNodes[i];
      setCursorPosition(currentNode, range, stat);
    }
  }
  return range;
}
.highlight {
  color: #bf0808;
  background: #b6b6b9;
  font-weight: bold;
}

#code__space {
  border: 1px solid #ccc;
  min-height: 100px;
  padding: 10px;
  direction: ltr;
}
<div name="code-space" id="code__space" contenteditable="true" style="border: 1px solid blue; min-height: 2em"></div>
amel
  • 1,098
  • 2
  • 4
  • 17
  • Yes, but innerHTML isn't good for this because after each text formatting, the cursor is at the beginning of the page. – Nazar Feb 28 '23 at 08:35
  • 1
    @Nazar I see, I updated my solution please check it out, I was based on this solution if you want to understand it well `https://stackoverflow.com/questions/69956977/html-contenteditable-keep-caret-position-when-inner-html-changes` – amel Feb 28 '23 at 09:41