I'm working on a little experimental editor where I would like to visualize the time between typed characters. Therefore I'm using javascript and a contenteditable div to wrap every character with a SPAN and a timestamp attribute. I build a little function with the help of rangy:
function insertAtCursor(char, timestamp) {
var sel = rangy.getSelection();
var range = sel.rangeCount ? sel.getRangeAt(0) : null;
if (range) {
var el = document.createElement("span");
$(el).attr('time', timestamp);
el.appendChild(document.createTextNode(char));
range.insertNode(el);
range.setStartAfter(el);
rangy.getSelection().setSingleRange(range);
}
}
Now I'm facing two problems with this concept where I would appreciate some help:
a. With the above function the output ends in nested span's like seen here:
<span time="12345">a
<span time="12345">b
<span time="12345">c</span>
</span>
</span>
b. Even if I could get the above function running, a copy&paste or drag&drop action would possibly also end in some nested span's ... and I wonder if there is a way to avoid that at all?
Thanks, Andreas
I'm not convinced this a good idea overall, particularly if the text could get large. A couple of improvements:
time
should probably be data-time
to validate as HTML5range.deleteContents()
would do).However, if you are going to do this, I would suggest checking if the cursor is at the end of a text node inside an existing <span>
and appending the new <span>
after the text node's parent. Something like this:
Live demo: http://jsfiddle.net/kWL82/1/
Code:
function insertAtCursor(char, timestamp) {
var sel = rangy.getSelection();
var range = sel.rangeCount ? sel.getRangeAt(0) : null;
var parent;
if (range) {
var el = document.createElement("span");
$(el).attr('data-time', timestamp);
el.appendChild(document.createTextNode(char));
// Check if the cursor is at the end of the text in an existing span
if (range.endContainer.nodeType == 3
&& (parent = range.endContainer.parentNode)
&& (parent.tagName == "SPAN")) {
range.setStartAfter(parent);
}
range.insertNode(el);
range.setStartAfter(el);
rangy.getSelection().setSingleRange(range);
}
}