JSFiddle - React, Tailwind, and code Playground

by timdown

HTML

<script src="http://rangy.googlecode.com/svn/trunk/dev/rangy-core.js"></script>
<script src="http://rangy.googlecode.com/svn/trunk/dev/rangy-textrange.js"></script>
<script src="http://rangy.googlecode.com/svn/trunk/dev/rangy-cssclassapplier.js"></script>
<div contenteditable="true" id="editor">Type in here. Character limit is 40.</div>

CSS

.overrun {
    background-color: #f66;
}

JavaScript

var characterLimit = 40;
var debounceInterval = 200;

function highlightExcessCharacters() {
    // Bookmark selection so we can restore it later
    var sel = rangy.getSelection();
    var savedSel = sel.saveCharacterRanges(editor);
    
    // Remove previous highlight
    var range = rangy.createRange();
    range.selectNodeContents(editor);
    var editorCharCount = range.text().length;
    classApplier.undoToRange(range);
    
    // Create a range selecting the excess characters
    range.selectCharacters(editor, characterLimit, editorCharCount);
    
    // Highlight the excess
    classApplier.applyToRange(range);
    
    // Restore the selection
    sel.restoreCharacterRanges(editor, savedSel);
}

var handleEditorChangeEvent = (function() {
    var timer;
    
    function debouncer() {
        if (timer) {
            timer = null;
        }
        highlightExcessCharacters();
    }
    
    return function() {
        if (timer) {
            window.clearTimeout(timer);
        }
        timer = window.setTimeout(debouncer, debounceInterval);
    };
})();

function listen(target, eventName, listener) {
    if (target.addEventListener) {
        target.addEventListener(eventName, listener, false);
    } else if (target.attachEvent) {
        target.attachEvent("on" + eventName, listener);
    }
}

rangy.init();
var editor = document.getElementById("editor");
var classApplier = rangy.createClassApplier("overrun");

// Set up debounced event handlers
var editEvents = ["input", "keydown", "keypress", "keyup", "cut", "copy", "paste"];
for (var i = 0, eventName; eventName = editEvents[i++]; ) {
    listen(editor, eventName, handleEditorChangeEvent);
}