m9dfukc
m9dfukc

Reputation: 13

Replacing contenteditable characters on the fly with (or without) rangy

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

Upvotes: 0

Views: 992

Answers (1)

Tim Down
Tim Down

Reputation: 324627

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 HTML5
  • you need to handle the case where some content is selected (adding range.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); 
    } 
}

Upvotes: 1

Related Questions