1

I am making a WYSIWYG editor using contenteditable. Instead of using the caret that the browser displays, I need to draw my own simulated caret (to work around browser bugs). I need to get the exact position of the blinking caret, so I can draw my own caret at that position.

A call to document.getSelection().getRangeAt(0).getClientRects() almost works, but there's one edge case: when the caret is at the start or end of a line. See this demo:

const caretEl = document.getElementById('caret');

const updateFakeCaret = () => {
  const sel = document.getSelection();
  if (!sel) return undefined;
  if (sel.rangeCount !== 1) return undefined;
  const range = sel.getRangeAt(0);
  const clientRectList = range.getClientRects();
  const selectionRect = clientRectList.item(0);
  if (!selectionRect) return undefined;

  caretEl.style.top = selectionRect.top + window.scrollY + 'px';
  caretEl.style.left = selectionRect.left + window.scrollX + 'px';
  caretEl.style.width = selectionRect.width + 'px';
  caretEl.style.height = selectionRect.height + 'px';
};

document.addEventListener('selectionchange', updateFakeCaret);
window.addEventListener('resize', updateFakeCaret);
.editor {
  padding: 1em;
  white-space: pre-wrap;
  max-width: 20em;
  background-color: lightgrey;
}

#caret {
  position: absolute;
  z-index: 999;
  pointer-events: none;
  outline: 1px solid red;
}
<div class=editor contenteditable>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</div>
<div id=caret></div>

When you click at the end of line 2, the cursor appears at the end of line 2. When you click at the start of a line 3, the cursor appears at the start of line 3. But those are the exact same location! Using the document.getSelection API, both positions appear identical, returning the same two client rectangles. But they're clearly not identical, because the browser (at least Chrome and Firefox) draws its own caret in different positions depending on the user interaction.

Is there a way to distinguish between "caret at end of line" and "caret at start of line"? Is there otherwise a reliable way to get the exact screen position of the caret?

jameshfisher
  • 34,029
  • 31
  • 121
  • 167

0 Answers0