How to get the caret position on a textarea element in pixels

Let's assume that you have a <textarea> component and that you want to get the caret position of it in pixels:

html
<textarea onkeyup = 'getCaretPos(event)' id = 'textarea' rows = '1'></textarea>

The idea is to create a dummy element and to play with the dimensions/coordinates of it using getClientsRects() and getBoundingClientRect().

javascript
const getCaretPos = (e) => {
    
    // Getting the textarea element
    let textarea = document.getElementById('textarea');  

	// Adjusting textarea to prevent scrolling
	textarea.style.height = `${e.target.scrollHeight}px`
    
    // Appending element to the DOM after textarea
    textarea.insertAdjacentHTML('afterend', `<span id = 'dummy'>${textarea.value}</span>`);
    
    // Getting position info of the rectangles inside dummy element
    let rectangles = document.getElementById('dummy').getClientRects();
	let last = rectangles[rectangles.length - 1];
    
	// Getting position info of the textarea
	let text = document.getElementById('textarea').getBoundingClientRect();
    
    // Setting coordinates
    let x = last.x + last.width;
    let y = text.y + text.height - last.height;
	
	// Removing dummy
	dummy.remove();
    
    // Returning variables
    return [x, y];
    
}

And here is the CSS:

CSS
body{
	width: 200px;
}
#dummy, #textarea{
	box-sizing: border-box;
    font: 14px/1 monospace;
    border: 1px solid #999;
    white-space: pre-wrap;
    margin: 0;
	overflow: hidden;
    width: 100%;
}

Optional

You could add a function to draw a marker on top of the caret to verify if the returning variables are correct.

Example:

javascript
const drawSquare = (x, y) => {
	
	// Deleting previous square;
	if(document.getElementById('square')) 
		document.getElementById('square').remove();
	
	// Inserting new one
    textarea.insertAdjacentHTML('afterend', `<div id = 'square'></div>`);
	
	// Modifying position of it
	document.getElementById('square').style.left = `${x}px`;
	document.getElementById('square').style.top  = `${y}px`;
	
}

And here is the CSS:

CSS
#square{
	background: #4CAF50;
	border-radius: 50%;
	height: 5px;
	position: absolute;
	width: 5px;
}

You can see the full example at CodePen.

Hi, I'm Erik, an engineer from Barcelona. If you like the post or have any comments, say hi.