JSFiddle - React, Tailwind, and code Playground
HTML
<div id="p" contenteditable="true">I like <i>this</i> thing</div>
<small id='log'>caret position: [type something]</small>
CSS
div{ margin:10px; height:1.5em; padding:10px; border:3px solid #CCC; outline:none; position:relative; }
textarea{ color:transparent; position:absolute; top:0; left:0; bottom:0; right:0; border:0; padding:10px; outline:none; }
.exceeds{ color:#CCC; }
small{ padding:10px; font-size:12px; color:blue; }
JavaScript
$('div').on('focus', function(){
var that = this;
// USER WRITES SOMETHING
$(document).on('keypress', function(e){
var caretPos = getCaretCharacterOffsetWithin(that);
$('#log').text('caret position: ' + caretPos);
// TEXT IS PARSED AND HTML IS CHANGED ACCORDINGLY
that.innerHTML = wordsLimiter( $(that).text() );
// NOW NEED TO RESTORE CARET POSITION (REMEMBER - HTML HAS CHANGED)
});
});
// stop listening to keypresses
$('div').on('blur', function(){
$(document).off('keyup keydown');
});
// validate words and returned parsed result
function wordsLimiter(s){
var maxWords = 5,
splitted = s.split(' '),
output = splitted.slice(0,maxWords - 1).join(' '),
exceededWordsArr = splitted.slice(maxWords -1);
if( exceededWordsArr.length ){
output += '<span class="exceeds"> ' + exceededWordsArr.join(" ") + '</span>';
}
return output;
}
//-------- CARET METHODS --------//
function placeCaret(el) {
el.focus();
if (typeof window.getSelection != "undefined" && typeof document.createRange != "undefined") {
//var range = document.createRange();
//range.selectNodeContents(el);
//range.collapse(false);
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
} else if (typeof document.body.createTextRange != "undefined") {
var textRange = document.body.createTextRange();
textRange.moveToElementText(el);
textRange.collapse(false);
textRange.select();
}
}
function getCaretCharacterOffsetWithin(element) {
var caretOffset = 0;
var doc = element.ownerDocument || element.document;
var win = doc.defaultView || doc.parentWindow;
var sel;
if (typeof win.getSelection != "undefined") {
var range = win.getSelection().getRangeAt(0);
var preCaretRange = range.cloneRange();
preCaretRange.selectNodeContents(element);
...