How to get the pixel offset from the current caret position in an iframe with contentEditable

前端 未结 2 772
爱一瞬间的悲伤
爱一瞬间的悲伤 2021-01-02 02:13

I would like to position a floating div element in an iframe with contentEditable, in case the user enters a certain key combination (

相关标签:
2条回答
  • 2021-01-02 03:17

    The only reliable way of doing this is to insert a temporary element at the caret (ensuring that it is zero width), get its position and remove it again. You should also glue the two ends of the text node (if it was a text node that contained the caret) back together to ensure the DOM is as it was before inserting the node. Note, however, that doing this (or any other manual DOM manipulation on the editable content) breaks the browser's internal undo stack.

    The reason for this is that careful reading of the spec for the getBoundingClientRect() method of Range shows that getBoundingClientRect() is not obliged to return a Rect for a collapsed Range. Conceptually, not every position in the document has a well-defined bounding rectangle. The caret, however, does have physical location on the screen which in my opinion should be provided by the Selection API, but currently there is nothing in browsers to provide this.

    0 讨论(0)
  • 2021-01-02 03:18

    I came into this problem today. After some testing, I got this working, without using temorary element.

    In IE, it's easy to work it out with offsetLeft and offsetTop property of a TextRange object. Some effort is needed for webkit though.

    Here's a test, you can see the result. http://jsfiddle.net/gliheng/vbucs/12/

    var getCaretPixelPos = function ($node, offsetx, offsety){
        offsetx = offsetx || 0;
        offsety = offsety || 0;
    
        var nodeLeft = 0,
            nodeTop = 0;
        if ($node){
            nodeLeft = $node.offsetLeft;
            nodeTop = $node.offsetTop;
        }
    
        var pos = {left: 0, top: 0};
    
        if (document.selection){
            var range = document.selection.createRange();
            pos.left = range.offsetLeft + offsetx - nodeLeft + 'px';
            pos.top = range.offsetTop + offsety - nodeTop + 'px';
        }else if (window.getSelection){
            var sel = window.getSelection();
            var range = sel.getRangeAt(0).cloneRange();
            try{
                range.setStart(range.startContainer, range.startOffset-1);
            }catch(e){}
            var rect = range.getBoundingClientRect();
            if (range.endOffset == 0 || range.toString() === ''){
                // first char of line
                if (range.startContainer == $node){
                    // empty div
                    if (range.endOffset == 0){
                        pos.top = '0px';
                        pos.left = '0px';
                    }else{
                        // firefox need this
                        var range2 = range.cloneRange();
                        range2.setStart(range2.startContainer, 0);
                        var rect2 = range2.getBoundingClientRect();
                        pos.left = rect2.left + offsetx - nodeLeft + 'px';
                        pos.top = rect2.top + rect2.height + offsety - nodeTop + 'px';
                    }
                }else{
                    pos.top = range.startContainer.offsetTop+'px';
                    pos.left = range.startContainer.offsetLeft+'px';
                }
            }else{
                pos.left = rect.left + rect.width + offsetx - nodeLeft + 'px';
                pos.top = rect.top + offsety - nodeTop + 'px';
            }
        }
        return pos;
    };
    
    0 讨论(0)
提交回复
热议问题