Search code examples
javascriptcontenteditablecaretcursor-position

Set caret position in contenteditable div


Intro

When editing the content of a contenteditable DOM Object, different browsers have different behaviours. For instance, Firefox 18.0 creates a new paragraph (<p>) or a line break <br/> in some instances while Chrome 24 creates a <div>.

In order to cope with this, I'm listening to the DOMNodeInserted event and replacing the new inserted nodes with p tags.


Problem

The problem is placing the caret in place. I've read tons of posts in SO regarding this same subject but none of the provided answers have worked, at least in Chrome 24.


Code

JSFiddle

obj.addEventListener("DOMNodeInserted", onNodeInsert, false);

function onNodeInsert(e) {
    var range    = document.createRange(),
        sel      = window.getSelection(),
        newNode  = e.target,
        tagName  = newNode.tagName.toLowerCase(),
        lnbrNode = document.createElement('br'),
        pNode    = document.createElement('p');

    if (tagName === 'div' && newNode.getAttribute("id") === null) {
        // First we remove the event listener so that it doesn't get triggered again
        this.removeEventListener('DOMNodeInserted', onNodeInsert, false);

        // Creates a p node and removes the div
        newNode.parentNode.replaceChild(pNode, newNode);
        pNode.appendChild(lnbrNode);

        // Places the caret where it belongs
        range.setStart(pNode, 0);
        sel.removeAllRanges();
        sel.addRange(range);    

        //We can restore the event listener now
        this.addEventListener("DOMNodeInserted", onNodeInsert, false);
    }
}

Solution

  • For those who might encounter the same problem as I do, here's my dirty fix...


    Relevant code

    JSFiddle

    obj.addEventListener("DOMNodeInserted", onNodeInsert, false);
    
    function onNodeInsert(e) {
        var range    = document.createRange(),
            sel      = window.getSelection(),
            newNode  = e.target,
            tagName  = newNode.tagName.toLowerCase(),
            lnbrNode = document.createElement('br'),
            pNode    = document.createElement('p');
    
        if (tagName === 'div' && newNode.getAttribute("id") === null) {
            // First we remove the event listener so that it doesn't get triggered again
            this.removeEventListener('DOMNodeInserted', onNodeInsert, false);
    
            // Creates a p node and removes the div
            newNode.parentNode.replaceChild(pNode, newNode);
            pNode.appendChild(lnbrNode);
    
            // Places the caret where it belongs
            var placeCursor = function () {
                range.setStart(pNode, 0);
                sel.removeAllRanges();
                sel.addRange(range);    
            }
    
            //placeCursor(); // DOES NOT WORK (cursor disappears)
            setTimeout(placeCursor,1); // WORKS
    
            //We can restore the event listener now
            this.addEventListener("DOMNodeInserted", onNodeInsert, false);
        }
    }