3

Suppose I have a plain HTML textarea. What I want to do is I want to be able to insert a different character when a character is typed. For example, I want to insert <> when < is typed into the textarea. Also, I want to be able to move the cursor between the < and > if possible.

Here's what I have for now. It only works if the cursor is at the end though.

let editing = document.querySelector("#editing");

const handler = (event) => {
  if (editing.value.endsWith("<")) {
    editing.value += ">";
    editing.setSelectionRange(editing.selectionStart - 1, editing.selectionStart - 1)
  }
}

editing.addEventListener("propertychange", handler)
editing.addEventListener("input", handler)
<textarea name="editing" id="editing"></textarea>
  • 1
    Is this what you're looking for [How to get the caret column (not pixels) position in a textarea, in characters, from the start?](https://stackoverflow.com/questions/263743/how-to-get-the-caret-column-not-pixels-position-in-a-textarea-in-characters) – Charlie Bamford Jun 16 '21 at 14:14
  • @CharlesBamford It kind of does, I'll see – Siddharth Shyniben Jun 16 '21 at 14:20
  • `event.data` has the info you're looking for, look at [mdn](https://developer.mozilla.org/en-US/docs/Web/API/InputEvent) for docs. Also use `event.target` instead of `editing`, it'll make your handler reusable. – yi fan song Jun 16 '21 at 14:37

1 Answers1

2

See Use tab to indent in textarea for very related discussion, which this answer is based on.

The following code should do what you want, by capturing the keydown event for < and replacing it with the insertion of <> (overwriting whatever the current selection is) and moving the cursor after the <:

const editing = document.querySelector("#editing");

editing.addEventListener('keydown', (e) => {
  if (e.key === '<') {
    e.preventDefault();
    const pos = editing.selectionStart;
    editing.value = editing.value.slice(0, pos) + '<>' +
      editing.value.slice(editing.selectionEnd);
    editing.selectionStart = editing.selectionEnd = pos + 1;
  }
});

Unfortunately, this approach also trashes the undo stack of the editor. On Chrome, you can use document.execCommand('insertText', false, '<>') to simulate typing <>. Unfortunately, this is not supported on Firefox, so it's not a great solution.

edemaine
  • 2,699
  • 11
  • 20
  • 1
    Wow that note about the undo stack lead me down a whole rabbit hole about preserving the undo stack. Supposedly, `execCommand` should be fixed in firefox 89 if [mdn](https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand#browser_compatibility) is correct. I can't believe the only way to edit a `textarea` value without killing the undo stack is to rely on deprecated apis. – yi fan song Jun 16 '21 at 14:55