0

I have a contenteditable element what I update/filter with javascript. After updating the textcontent the but the caret always goes back to the beginning. A want to make to work like the input element. I set up a demo:

  1. test1 - contenteditable element
  2. test2 - input element

In this example I have a function what enable to insert only numbers, if you insert other characters nothing will show.

After entering some numbers to the contenteditable and inserting a NOT number, then the cursor (caret) goes to the start. In case of input element the cursor goes to the end.

How to achieve that, to remain the cursor to the position where it was? (pure javascript)

function onlyNumber(element) {
  const invalidChars = /\D/g;
  ob = element.target;
  if (element.target.nodeName == "INPUT") {
    if (invalidChars.test(ob.value)) {
      ob.value = ob.value.replace(invalidChars, "");
    }
  } else if (element.target.nodeName == "TD") {
    if (invalidChars.test(ob.textContent)) {
      ob.textContent = ob.textContent.replace(invalidChars, "");
    }
  }
}

document.getElementById("test1").addEventListener("input", function(event) {
  onlyNumber(event);
})
document.getElementById("test2").addEventListener("input", function(event) {
  onlyNumber(event);
})
#test1 {
  border: 1px solid gray;
  padding: 5px;
  width: 100px;
}

#test2 {
  margin-top: 15px;
}
<table class="table">
  <tbody>
    <tr>
      <td id="test1" contenteditable="true"></td>
    </tr>
  </tbody>
</table>
<input id="test2" />
user348246
  • 380
  • 4
  • 16

1 Answers1

1

You could use the keydown event, and prevent all but numbers and arrows/delete/backspace.

Stack snippet

function onlyNumber(element) {
  const invalidChars = /\D/g;
  ob = element.target;
  if (element.target.nodeName == "INPUT") {
    if (invalidChars.test(ob.value)) {
      ob.value = ob.value.replace(invalidChars, "");
    }
  } else if (element.target.nodeName == "TD") {
    if (invalidChars.test(ob.textContent)) {
      ob.textContent = ob.textContent.replace(invalidChars, "");
    }
  }
}

document.getElementById("test1").addEventListener("keydown", function(event) {
  if ((event.keyCode < 58 && event.keyCode > 47) ||
      (event.keyCode < 41 && event.keyCode > 36) ||
      event.keyCode == 8 || event.keyCode == 46) {
    return true;
  }
  event.preventDefault();
})
document.getElementById("test2").addEventListener("input", function(event) {
  onlyNumber(event);
})
#test1 {
  border: 1px solid gray;
  padding: 5px;
  width: 100px;
}

#test2 {
  margin-top: 15px;
}
<table class="table">
  <tbody>
    <tr>
      <td id="test1" contenteditable="true"></td>
    </tr>
  </tbody>
</table>
<input id="test2" />

Another option would be to re-position the cursor:

Asons
  • 84,923
  • 12
  • 110
  • 165
  • thanks your resolution, but I dont want position to the end, and I want to use input, to can copy, paste, drag numbers to contenteditable – user348246 May 08 '18 at 08:18
  • @almostokey Updated my answer with one more link. To accomplish what you ask is not trivial, and if users e.g. paste, they normally does that with all numbers at once, hence move cursor to the end will be fine. Still, as the link suggests, a lot of work to maintain that. – Asons May 08 '18 at 08:41
  • Paste should logicall as-if inserting a single character and the caret should be and the end of pasted region after the event. If your data model wants to remove non-whitelisted characters in between, that can logically happen after that. However, removing individual letters from the same text node with the current caret position is without messing *relative* caret position is far from trivial. Basically you have to get current range/selection and compute it as element + character offset, remove N characters, and fix the offset by N characters and the re-create the logically same selection. – Mikko Rantalainen Nov 24 '22 at 10:39