Hier mal eine Variante dessen, was ich gebaut habe. Das Auslesen klappt, beim Setzen gibt es immer den Fehler (Zum Ausprobieren kannst Du einfach irgendwo im Test-String eine Eingabe machen, dann wird die Caret-Position angezeigt. Wenn Du anschließend auf den Button klickst, wird das innerHTML ersetzt und der Cursor sollte danach wieder auf die richtige Position geschoben werden, was aber leider nicht passiert).
<div id="container"></div>
<div id="pos"></div>
<input type="button" onclick="javascript:replace_content();" value="test"/>
<script type="text/javascript">
var ed = document.createElement('pre');
ed.innerHTML = '<span class="other"><span class="preproc"><?php</span></span><span class="php"><span class="function"> echo </span><span class="var">$test</span><span class="interpunction">;</span><span class="preproc">?></span></span>';
ed.contentEditable = true;
document.getElementById('container').appendChild(ed);
ed.onkeyup = function(e) {
var sel = window.getSelection();
var range = document.createRange();
range.setStart(ed, 0);
range.setEnd(sel.anchorNode, sel.anchorOffset);
var caret_pos = range.toString().length;
document.getElementById('pos').innerHTML = caret_pos;
}
function replace_content() {
ed.innerHTML = '<span class="other"><span class="preproc"><?php</span></span><span class="php"><span class="function"> echo </span><span class="var">$test</span><span class="interpunction">;</span></span>';
var caret_pos = parseInt(document.getElementById('pos').innerHTML);
var ptr = 0;
outer: for (var i = 0; i < ed.childNodes.length; i++) {
for (var j = 0; j < ed.childNodes[i].childNodes.length; j++) {
var tmp = ed.childNodes[i].childNodes[j];
if (tmp.textContent.length + ptr < caret_pos) {
ptr += tmp.textContent.length;
continue;
}
else {
var new_anchor = tmp;
var anchor_offset = caret_pos - ptr;
break outer;
}
}
}
if (new_anchor) {
console.log('Anchor Content: "' + new_anchor.innerHTML + '"');
console.log('Anchor Offset' + anchor_offset);
var sel = window.getSelection();
var new_range = document.createRange();
new_range.setStart(new_anchor, anchor_offset);
new_range.setEnd(new_anchor, anchor_offset);
sel.addRange(new_range);
}
}
</script>