views:

35

answers:

1

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.

+1  A: 

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!)

bobince
Thanks for your prompt and considered answer. You won't believe how many different hacks and workarounds I've attempted while trying to get this to work. Actually, if you have experience of IE, you probably have a fair idea. Good to hear that IE9 finally adopts the standard model. With any luck I should have all my users migrated by 2019...
Mike
Hello! I'm pretty certain that it's impossible to place the caret inside an empty inline element in IE. More generally, in IE it's impossible to place the caret at the start of the first text node within an element. This is also true in WebKit: https://bugs.webkit.org/show_bug.cgi?id=15256#c4. Adding whitespace is as good as it gets, I think. I have resorted to inserting a Unicode BOM character (`U+FEFF`, renders as zero width in all major browsers) at the start of a text node, which works but leaves you the logistical nightmare of removing the BOMs when the caret moves elsewhere.
Tim Down