JSFiddle - React, Tailwind, and code Playground

HTML

Iframe:<br />
<iframe id="text"></iframe>

<textarea style="display: none;" id="iframeDoc">
    <html>
	<head>
	<meta charset="utf-8">
        <style>
        body {
          margin: 0px;
          padding: 0px;
          font-family: 'Open Sans', 'Arial', sans-serif;
          font-size: 13px;
          line-height: 26px;
          }
        .invalid {
          background-color: #CC9999;
        }
        </style>
    </head>
    <body contenteditable="true"> Какой то текст <span class="invalid">(-blabla-)</span></body>
    </html>
</textarea>

CSS

.invalid {
    background-color: #CC7777;
}

JavaScript

$(function(){
	isIE = /*@cc_on!@*/false;
     
    frame = $('#text').get(0);
    frameDoc = isIE ? frame.contentWindow.document : frame.contentDocument;
    frameDoc.open();
    frameDoc.write($('#iframeDoc').text());
    frameDoc.close();
    frameDoc.designMode = "on";
    
    $(frameDoc).on('keydown', 'body', function(event) {
            
        var keyCodesList = [ 8, 35, 36, 37, 38, 39, 40, 46 ], // Список клавиш на которые не нужно реагировать
            selection = frame.contentWindow.getSelection();

        if ($.inArray(event.keyCode, keyCodesList) == -1) {
            // Если Node уже помечен как невалидный Xslate (родитель редактируемого элемента не body)
            if (selection.isCollapsed &&
                selection.anchorNode.parentElement != document.body &&
                selection.anchorOffset == selection.anchorNode.length) {

                var curRange = selection.getRangeAt(0),
                    range = document.createRange(),
                    Span = document.createElement('span');

                Span.innerHTML = "&nbsp";
                $(Span).insertAfter(curRange.endContainer.parentElement);

                range.selectNode(Span);
                selection.removeAllRanges();
                selection.addRange(range);
            }
        }
    });
});