Tivie
Tivie

Reputation: 18923

Why does this code work when called in setTimeout but not outside it?

I have an eventHandler that is trigered every time a node is inserted in a contenteditable div. This eventHandler replaces a newly inserted div node with a p node.

The problem is placing the cursor after the replacement. Basically the cursor isn't placed properly, that is, the cursor disappears while it was expected to be placed in the new created p node. Strangely the content editable div still has focus and dumping the window.selection object shows the range correctly set.

The only way I could workaround this issue was with a dirty fix using setTimeout.


Q. Why does it work when placeCursor() is called with setTimeout but not without it?


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);
    }
}

for more context see this post

Upvotes: 3

Views: 208

Answers (1)

Gabriel Gartz
Gabriel Gartz

Reputation: 2870

I don't know how to do a good explain, but is because the DOM persistence delay. I have lot of problems with mobile. So the dom isn't ready when the listener callback is executed.

So it wont work because you are trying to manage a selection. If you put a timeout, that function will be execute after 4ms, so it's enough time to DOM persist your changes with range and make the changes you are trying to do.

Indeed the browser should not need defer the executing of function to work, in theory your code is correct without the setTimeout, but this "bug" you can find in many ways when working with listeners that are triggered by DOM changing.

Just to you know. In my desktop browser, it work like it should, but, when I ran it in my netbook, I need to use setTimout to work.

Upvotes: 2

Related Questions