Search code examples
javascripthtmlinternet-explorer-8contenteditablerangy

contenteditable div: IE8 not happy with backspace remove of HTML element


I am making use of a contenteditable div in combination with the rangy Javascript library to insert HTML at the cursor position.

End of the day the contents of the div commonly looks like:

<div contenteditable="true">
    "Hello "
    <button contenteditable="false" data-id="147">@John Smith</button>
    " "
</div>

Users get suggested upon pressing '@' and get subsequently inserted as a button when selected (ala Google Plus). I also insert a &nbsp; after this button.

The button gets removed in Chrome/Safari/Firefox when you hit backspace (after first removing the &nbsp;), but not in IE8. In IE8 the cursor merely jumps over the button without removing it. What's even more bizarre in IE8 is if you leave the &nbsp; next to the button - and rather place the cursor right next to the button - it removes the button on backspace. So it's happy when there's a &nbsp; to the right of the cursor.

Does anyone know what I need in order to make IE8 work i.t.o. removing the button upon backspace without the need for a &nbsp; to the right of the cursor? (some info on this strange behaviour might also help)

P.S. I haven't tested other versions of IE


Solution

  • My suggestion would be to first check the caret is positioned after the non-editable node, and if so, create a range that starts immediately after the non-editable element and ends at the caret position. Check whether the text in this range is empty. If it is, that means the caret is placed directly after the non-editable element, so in that case remove the element. Finally, place the caret where the non-editable had been.

    Live demo: http://jsfiddle.net/timdown/vu3ub/

    Code:

    document.onkeypress = function(e) {
        e = e || window.event;
        var keyCode = e.which || e.keyCode;
        if (keyCode !== 8) {
            return;
        }
    
        var sel = rangy.getSelection();
        if (sel.rangeCount === 0) {
            return;
        }
    
        var selRange = sel.getRangeAt(0);
        if (!selRange.collapsed) {
            return;
        }
    
        var nonEditable = document.getElementById("nonEditable");
        if (!nonEditable) {
            return;
        }
    
        // Check the caret is located after the non-editable element
        var range = rangy.createRange();
        range.collapseAfter(nonEditable);
    
        if (selRange.compareBoundaryPoints(range.START_TO_END, range) == -1) {
            return;
        }
    
        // Check whether there is any text between the caret and the
        // non-editable element. If not, delete the element and move
        // the caret to where it had been
        range.setEnd(selRange.startContainer, selRange.startOffset);
        if (range.toString() === "") {
            selRange.collapseBefore(nonEditable);
            nonEditable.parentNode.removeChild(nonEditable);
            sel.setSingleRange(selRange);
    
            // Prevent the default browser behaviour
            return false;
        }
    };