javascripttextcross-browsercontenteditable

Setting the caret position to an empty node inside a contentEditable element


My task is to set a text caret to appear inside an empty span node within a contentEditable div.

The following gives me no problems on Firefox 3.6:

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <script type="text/javascript" src="js/jquery-1.4.3.min.js">
        </script>
        <style>
            #multiple {
                border: 1px solid #ccc;
                width: 800px;
                min-height: 20px;
                padding: 5px;
                outline: none;
            }
        </style>
        <script>
            $(document).ready(function(){
                var contentEditable = document.getElementById('multiple');
                var lastItem = contentEditable.getElementsByTagName('span').item(2);
    
                var selectElementText = function(el, win){
                    win = win || window;
                    var doc = win.document, sel, range;
                    if (win.getSelection && doc.createRange) {                    
                        range = doc.createRange();
                        range.selectNodeContents(el);
                        range.collapse(false);
                        sel = win.getSelection();
                        sel.removeAllRanges();
                        sel.addRange(range);
                    }
                    else 
                        if (doc.body.createTextRange) {
                            range = doc.body.createTextRange();
                            range.moveToElementText(el);
                            range.select();
                        }
                }

                contentEditable.focus();
                selectElementText(lastItem);
            });
        </script>
        <title>Range Selection Tasks (Make Me Want to Cry)</title>
    </head>
    <body>
        <div id="multiple" contentEditable="true">
            <span style="color:red">First</span><span style="color:green">Second</span><span style="color:blue"></span>
        </div>
    </body>
</html>

... but on Webkit and IE, the focus is set to the penultimate span. No idea why. It works if I put a space inside the last span, but then I get a one-character range selection.

Having said that, it's acceptable to have whitespace in the last node if the caret is at the very start.

Any help with this would be greatly appreciated. Thank you in advance.


Solution

  • IE's selection/range model is based around indexes into text content, disregarding element boundaries. I believe it may be impossible to set the input focus inside an inline element with no text in it. Certainly with your example I cannot set focus inside the last element by clicking or arrow keys.

    It almost works if you set each span to display: block, though there's still some highly strange behaviour, dependent on the existence of whitespace in the parent. Hacking the display to look inline with tricks like float, inline-block and absolute position make IE treat each element as a separate editing box. Relative-positioned block elements next to each other work, but that's probably impractical.

    If it makes you feel any better, IE9 finally fixes this unpleasantness and adopts the standard range model. (Hooray!)

    it's acceptable to have whitespace in the last node if the caret is at the very start.

    I'd probably do that, then, unless an IE selection expert can think of anything better. (Calling Tim Down!)