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 parentLetter(node) {
var letter = node;
while (letter.nodeType != 6
&& letter.tagName !== "SPAN"
&& letter.parentNode)
letter = letter.parentNode;
return letter;
}
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;
var oldRange = null;
function saveRange(selection) {
if (!selection)
oldRange = null;
else
oldRange = selection.saveCharacterRanges($('#foo')[0]);
}
function loadRange(selection) {
if (!oldRange) return;
selection.restoreCharacterRanges($('#foo')[0],oldRange);
}
function downHandler(placeholder) { return function($e){
var sel = rangy.getSelection();
if (!isAlpha($e.which))
saveRange(sel);
else if (sel.isCollapsed) {
if (!!oldRange)
breakOutLastChar(sel);
else
saveRange(sel);
}
else
console.log("uncollapsed selections?!?");
};}
function upHandler($e) {
if (!isAlpha($e.which))
saveRange(null);
else if (!!oldRange)
breakOutLastChar(rangy.getSelection());
}
function breakOutLastChar(sel) {
var nr = sel.getRangeAt(0).cloneRange();
loadRange(sel);
var or = sel.getRangeAt(0).cloneRange();
console.log("or: ",or);
console.log("nr: ",nr);
var r = rangy.createRange();
...