David
David

Reputation: 4873

Removing a node while keeping all the children and the selection

So, I'm trying to keep a selection while changing the DOM around. I have the first half working perfectly, where when I add it keeps it selected, but when removing it's not working so well. Depending what is selected depends whether or not it works.

You can see an example here: http://jsfiddle.net/zanpm28d/

If you select "Select Me", you can click the button all day, and it will go in and out, and keep the selection. However, if you just select "Select" or "Don't Select Me" it will lose the selection when going backwards.

Here is the relevant section of code.

else{        
    // Get the first selection
    var range = sel.getRangeAt(0);
    // Get what was selected as a fragment
    var frag = range.cloneContents();

    // Create a new block element
    var block = document.createElement(blockType.toLowerCase());

    // Take all the children of the fragment, and place them into the block
    // This will also remove them from the fragment
    while(frag.firstChild != undefined && frag.firstChild != null){
        block.appendChild(frag.firstChild);
    }

    // Place the block back into the fragment
    frag.appendChild(block);

    // Now kill what was originally selected
    range.extractContents();

    // And put back in what we just built
    range.insertNode(frag);

    // Then reselect what we had.
    sel.removeAllRanges();
    if(block.childNodes.length > 0){
        var newRange = document.createRange();
        newRange.setStart(block.childNodes[0], 0);
        newRange.setEnd(block.childNodes[block.childNodes.length - 1], block.childNodes[block.childNodes.length - 1].length);
        sel.addRange(newRange);
    }
    return true;
}

If you wondering why I'm doing this at all, IE. All other browsers keep the selection properly, but IE will for whatever reason not be right, and will put blocks inside of blocks if I don't change the selection "manually."

Upvotes: 0

Views: 213

Answers (1)

Tim Down
Tim Down

Reputation: 324567

Rather than using innerHTML and outerHTML, move the child nodes and then you can position a new selection range to encompass these nodes. Here's an updated version of your example:

http://jsfiddle.net/zanpm28d/1/

The crucial bit of code:

// Insert all of elemStart's child nodes before elemStart        
var firstChild = elemStart.firstChild;
var lastChild = elemStart.lastChild;
var child;
while ( (child = elemStart.firstChild) ) {
    elemStart.parentNode.insertBefore(child, elemStart);
}

// Remove elemStart so that its children have now replaced it
elemStart.parentNode.removeChild(elemStart);

// Reselect the contents of elemStart
var newRange = document.createRange();
newRange.setStartBefore(firstChild);
newRange.setEndAfter(lastChild);
sel.removeAllRanges()
sel.addRange(newRange);

Upvotes: 1

Related Questions