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
  • 16,928
  • 11
  • 80
  • 110
xem
  • 270
  • 1
  • 9

2 Answers2

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,232
  • 3
  • 43
  • 60
  • 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
  • 16,928
  • 11
  • 80
  • 110
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