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>
<span contenteditable=true id="foo" class="editable letter-container target"></span>
</p>
CSS
.red {color: red;}
.blue{color: blue;}
.editable {
border: 1px solid black;
border-radius: 5px;
padding: 5px;
}
.letter {
border: 1px solid #eee;
border-radius:2px;
padding: 1px;
}
.letter.new {
text-shadow: 2px 2px 5px #666;
}
}
/*
.letter.blank:before {
content: '\200B';
}
.letter.blank:after {
content: '\200B';
}
*/
JavaScript
function isAlpha(code) { return code>=65 && code<=90; }
function isMovement(code) { return code>=37 && code <=40; }
function isModifier(code) { return (code>=16 && code <=20) || code==91; }
function isDeletion(code) { return code==8 || code==46; }
function letterHTML(c) {
console.log("making letter for char " + c);
return "<span class='letter'>"+c+"</span>";
}
function makeNewLetter(c) { if (!c) c=''; return $(letterHTML(c)).addClass('new'); }
function makeBlankLetter() {return makeNewLetter('​');}
function parentLetter(node) {
var letter = node;
while (letter.nodeType != 6
&& letter.tagName !== "SPAN"
&& letter.parentNode)
letter = letter.parentNode;
if (letter.className.split(' ').indexOf('letter')<0)
return null;
return letter;
}
function updateFields() {
var source = $('#source').text();
var lettersHTML = source.split('')
.map(letterHTML)
.join('');
$('.editable').empty().append(lettersHTML);
$('span:even',$('#foo')).addClass('red');
$('span:odd',$('#foo')).addClass('blue');
}
$('#source').keydown(function($e){
if ($e.which==13) {
updateFields();
$e.preventDefault();
}
});
function downHandler($e) {
console.log("which: " + $e.which);
if (isMovement($e.which) || isModifier($e.which) || isDeletion($e.which))
return;
var sel = rangy.getSelection();
var r = sel.getRangeAt(0).cloneRange();
var anc = r.commonAncestorContainer;
var fN = sel.focusNode;
if (anc.nodeType == 3) {
console.log("focusing text node, text = " + anc.textContent);
var parent = parentLetter(fN);
if (parent) {
console.log("offset: " + sel.focusOffset);
var $parent = $(parent);
if ($parent.hasClass('new')) {
console.log("already in new letter");
}
else {
var $newLetter = makeBlankLetter();
if...