JSFiddle - React, Tailwind, and code Playground
HTML
<div id="main"></div>
CSS
.caretTrackingTextarea, .textareaMirror {
position:absolute;
top:0;
left:0;
font:14px/1 monospace;
padding:5px;
border:1px solid #999;
white-space:pre;
margin:0;
background:transparent;
width:300px;
max-width:300px;
}
.caretTrackingTextarea {
z-index:2;
min-height:200px;
}
.textareaMirror {
border-color:transparent;
}
.textareaMirror span {
opacity:0;
word-wrap: break-word;
overflow-wrap: break-word;
}
.textareaCaretTracker {
position:absolute;
width:4px;
height:4px;
background:#f00;
}
JavaScript
(window.trackCaret = function (Container) {
// log error when element is not passed
if (!Container) return;
var createElement = function (type, parent, className) {
var el = document.createElement(type);
parent.appendChild(el);
el.className = className;
return el;
},
textarea = createElement('textarea', Container, 'caretTrackingTextarea'),
textareaMirror = createElement('div', Container, 'textareaMirror'),
textareaMirrorInline = createElement('span', textareaMirror, ''),
textareaCaretTracker = createElement('div', Container, 'textareaCaretTracker'),
update = function () {
textareaMirrorInline.innerHTML = textarea.value.substr(0, textarea.selectionStart).replace(/\n$/, "\n\001");
var rects = textareaMirrorInline.getClientRects(),
lastRect = rects[rects.length - 1],
top = lastRect.top - textarea.scrollTop,
left = lastRect.left + lastRect.width;
textareaCaretTracker.style.top = top + "px";
textareaCaretTracker.style.left = left + "px";
},
addEvent = function (eventType) {
textarea["on" + eventType] = update;
};
addEvent('keyup');
addEvent('mouseup');
addEvent('scroll');
})();
var main = document.getElementById('main');
trackCaret(main);