Caret position inside textarea

by Mdot

HTML

Place your cursor inside the textarea and move the caret. Selecting text also works.<br>
Position is: <span>0%</span><br>
<textarea>


4. foo bar
5. bar
6. foo foo snoo
7. hello world
8. hey there
9. 
10. foo bar bar
11. bar


14.
15. goodbye

:)

    

</textarea>

CSS

textarea {
    height: 300px;
    width: 300px;
}

JavaScript

const textarea = document.querySelector('textarea');
const span = document.querySelector('span');

function get_current_caret_pos() {
	let current_line = 1;
    let char_count = 0;
    const lines = textarea.value.split(/\n/);
    // Accounts for actually selected text and calculates the mean.
    const selection_pos = Math.round((textarea.selectionStart + textarea.selectionEnd) / 2);
    
    for (let line_index = 0, line; line_index < lines.length; line_index++) {
	    line = lines[line_index];
        // +1 for new-line that we stripped with split().
    	char_count += line.length + 1;
		
        if (selection_pos < char_count) {
        	current_line = line_index + 1;
            break;
        }
    }
    
    // The "true" current line position would be:
    // return 100 / lines.length * current_line;
    // But since we have no 0th line, we interpolate for that.
    // So: 1st line = 0%, last line = 100%
    return (current_line - 1) / (lines.length - 1) * 100;
}

// selectionchange isn't input-specific so we have to check if it's our desired input.
// There is https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/selectionchange_event
// but so far, only Firefox has it implemented.
document.addEventListener('selectionchange', event => {
	if (document.activeElement === textarea) {
    	const caret_pos = get_current_caret_pos();
        span.textContent = `${new Intl.NumberFormat().format(caret_pos)}%`;
    }
});