JSFiddle - React, Tailwind, and code Playground
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>
<p>
source:
<span contenteditable=true id="source" class="editable">testing...</span>
<p>
without whitespace:
<span contenteditable=true id="foo" class="editable target"></span>
(doesn't work at all)
</p>
<p>
with whitespace:
<span contenteditable=true id="bar" class="editable target"></span>
(works, sort of)
</p>
CSS
.red {color: red;}
.blue{color: blue;}
.editable {
border: 1px solid black;
border-radius: 5px;
padding: 5px;
}
.letter {
border: 1px solid #aaa;
border-radius:2px;
background-color: #eee;
margin: 2px;
padding: 2px;
}
JavaScript
function isAlpha(code) { return code>=65 && code<=90; }
function newLetterHTML(c) {
return "<span class='letter'>"+c+"</span>";
}
function newLetter(c) { return $(newLetterHTML(c)); }
function updateFields() {
var source = $('#source').text();
var lettersHTML = source.split('').map(newLetterHTML).join('');
$('.editable').empty().append(lettersHTML);
$('span:even',$('#foo')).addClass('red');
$('span:odd',$('#foo')).addClass('blue');
$('span:even',$('#bar')).addClass('blue');
$('span:odd',$('#bar')).addClass('red');
}
$('#source').keydown(function($e){
if ($e.which==13) {
updateFields();
$e.preventDefault();
}
});
var $newChar = null;
function handler(placeholder) { return function($e){
if (!isAlpha($e.which))
return;
var sel = rangy.getSelection();
if (sel.isCollapsed) {
var parentElement = sel.focusNode.parentElement;
var offset = sel.focusOffset;
var r = sel.getRangeAt(0).cloneRange();
r.selectNode(parentElement);
r.collapse(offset==0);
$newChar = newLetter(placeholder);
r.insertNode($newChar[0]);
//$newChar.insertAfter($(parentElement));
r.selectNode($newChar[0]);
r.collapse(false);
sel.setSingleRange(r);
}
}}
$('#foo').keydown(handler(''));
$('#bar').keydown(handler(' '));
updateFields();