11

I need to update the innerHTML of a contenteditable P element after each keystroke, in JavaScript

(no jQuery)

I can't use an input or textarea instead of the P element.

It works fine, but the caret always goes back at the beginning of the paragraph when the innerHTML is reset.

I tried to use the solutions of the other SO questions that talk about carets and contenteditable but it doesn't seem to work in my case: I want to put the caret back exactly where it was before the update of innerHTML.

p.oninput=function(){

  // Get caret position
  c = window.getSelection().
      getRangeAt(0).
      startOffset;
  console.log(c);

  // Update innerHTML
  p.innerHTML = p.innerHTML.toUpperCase();

  // Place caret back
  // ???
}
p{ border: 1px dotted red }
<p contenteditable id=p>type here

BTW, It doesn't need to work on IE, but if you have a cross-browser solution, I'll take it too.

Thanks for your help!

Zze
  • 18,229
  • 13
  • 85
  • 118
xem
  • 300
  • 2
  • 9

3 Answers3

1

How late do you want to be to the party? "Yes"

If someone is still looking for a solution, maybe this works for you?

p.oninput=function(){

  // Get caret position
  c = window.getSelection().
  getRangeAt(0).
  startOffset;
  console.log(c);

  // Update innerHTML
  p.innerHTML = p.innerHTML.toUpperCase();

  var range = document.createRange();
  var sel = window.getSelection();
  range.setStart(p.childNodes[0], c);
  range.collapse(true);
  sel.removeAllRanges();
  sel.addRange(range);
}
p{ border: 1px dotted red }
<p contenteditable id=p>type here
pewpewlasers
  • 3,025
  • 4
  • 31
  • 58
0

Consider stepping away from the problem.

Solution 1: do not convert to uppercase until finished typing.

Solution 2: set P to monospaced font. Make the P transparent. Have div behind that updates with values as uppercase. Not sure how to show caret ... ?

Remember seeing article a year or so ago where a coding interface used similar method to have colour coded code in a textarea.

Ruskin
  • 5,721
  • 4
  • 45
  • 62
  • 1
    Hi, thanks for these ideas, however my transformation is doing more than just toUpperCase. I simplified it for the example. I need to make kind of a coding interface indeed, but inside a p (or pre) element, not a textarea, and my problem is just the caret position. – xem Jan 20 '14 at 13:19
  • look at how codepen and jsfiddle do it. And there are lots of StackOverflow answers like this topic. – Ruskin Jan 20 '14 at 13:34
-1

I added

const range = window.getSelection();
range.selectAllChildren(p);
range.collapseToEnd();

to your code and it seems solve the problem.

p.oninput=function(){

  // Get caret position
  c = window.getSelection().
      getRangeAt(0).
      startOffset;
  console.log(c);

  // Update innerHTML
  p.innerHTML = p.innerHTML.toUpperCase();

  const range = window.getSelection();
  range.selectAllChildren(p);
  range.collapseToEnd();
}
p{ border: 1px dotted red }
<p contenteditable id=p>type here
Zze
  • 18,229
  • 13
  • 85
  • 118
syc
  • 21
  • 3
  • I've added a code snippet to your answer. Please edit it so that we can actually see what you have done. – Zze Dec 12 '17 at 07:55
  • 1
    This does not work if you are editing the text anywhere which isn't at the end – Caltrop Jun 13 '21 at 15:43