简体   繁体   English

如何使用contentEditable从iframe中的当前插入位置获取像素偏移量

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

I would like to position a floating div element in an iframe with contentEditable , in case the user enters a certain key combination (for auto-complete purposes). 我想在iframe使用contentEditable定位浮点div元素,以防用户输入某个键组合(用于自动完成目的)。

I know how to get the caret position: document.getElementById('elm1_ifr').contentWindow.getSelection().anchorOffset 我知道如何获得插入位置: document.getElementById('elm1_ifr').contentWindow.getSelection().anchorOffset

I can use this to calculate the left property of the div, but I can't seem to figure out how to get the top . 我可以用它来计算div的left属性,但我似乎无法弄清楚如何获得top

Another possibility I thought about was using: document.getElementById('elm1_ifr').contentWindow.getSelection().anchorNode.parentNode 我想到的另一种可能性是: document.getElementById('elm1_ifr').contentWindow.getSelection().anchorNode.parentNode

And using jQuery to get the offset, but if that parent has a long text line, I would only be able to extract the top position of the first line. 并使用jQuery获取偏移量,但如果该父级有一个长文本行,我只能提取第一行的顶部位置。

Can anyone help me with this? 谁能帮我这个?

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. 您还应该将文本节点的两端(如果它是包含插入符的文本节点)粘合在一起,以确保DOM与插入节点之前一样。 Note, however, that doing this (or any other manual DOM manipulation on the editable content) breaks the browser's internal undo stack. 但请注意,执行此操作(或对可编辑内容进行任何其他手动DOM操作)会破坏浏览器的内部撤消堆栈。

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. 这样做的原因是仔细读取RangegetBoundingClientRect()方法的规范显示getBoundingClientRect()没有义务为折叠的Range返回Rect。 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. 但是,插入符确实在屏幕上有物理位置,我认为应该由Selection API提供,但目前浏览器中没有提供此功能。

I came into this problem today. 我今天遇到了这个问题。 After some testing, I got this working, without using temorary element. 经过一些测试,我得到了这个工作,没有使用temorary元素。

In IE, it's easy to work it out with offsetLeft and offsetTop property of a TextRange object. 在IE中,使用TextRange对象的offsetLeft和offsetTop属性很容易。 Some effort is needed for webkit though. 但是,webkit需要付出一些努力。

Here's a test, you can see the result. 这是一个测试,你可以看到结果。 http://jsfiddle.net/gliheng/vbucs/12/ 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;
};

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM