JSFiddle - React, Tailwind, and code Playground

HTML

<div id="info">
  
</div>
<div id="editable" contenteditable="true" onkeyup="getCaret()">

</div>

CSS

#editable {
  width: 100%;
  height: 400px;
  background: #333;
  color: #fff;
  padding: 20px;
}

#info {
  height: 60px;
}

JavaScript

function getCaret() {
	const editable = document.getElementById('editable');

  // collapse selection to end
  window.getSelection().collapseToEnd();

  const sel = window.getSelection();
  const range = sel.getRangeAt(0);

  // get anchor node if startContainer parent is editable
  let selectedNode = editable === range.startContainer.parentNode
    ? sel.anchorNode 
    : range.startContainer.parentNode;

  if (!selectedNode) {
  	document.getElementById('info').innerText = `caret: 0, line: 0`;
    return;
  }

  // select to top of editable
  range.setStart(editable.firstChild, 0);

  // do not use 'this' sel anymore since the selection has changed
  const content = window.getSelection().toString();
  const text = JSON.stringify(content);
  const lines = (text.match(/\\n/g) || []).length + 1;

  // clear selection
  window.getSelection().collapseToEnd();

  document.getElementById('info').innerText = `caret: ${text.length - 2}, line: ${lines}`;
}