从FF/Webkit中的像素位置创建折叠范围

Yan*_*roo 10 javascript position range

使用JavaScript,我想从像素位置创建折叠范围,以便在此位置标识的范围之后在文档流中插入新节点.

这可以使用Internet Exporer中的TextRange对象(moveToPoint(x,y)方法)来完成.

我怎么能在FireFox和Webkit中做到这一点?

我可以从document.elementFromPoint(x,y)的位置获取容器元素.但是当位置恰好位于文本节点内时,如何获得有关构建范围所需的文本偏移量的更多信息?

Val*_*gin 17

这是我caretRangeFromPoint对旧浏览器的实现:

if (!document.caretRangeFromPoint) {
    document.caretRangeFromPoint = function(x, y) {
        var log = "";

        function inRect(x, y, rect) {
            return x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom;
        }

        function inObject(x, y, object) {
            var rects = object.getClientRects();
            for (var i = rects.length; i--;)
                if (inRect(x, y, rects[i]))
                    return true;
            return false;
        }

        function getTextNodes(node, x, y) {
            if (!inObject(x, y, node))
                return [];

            var result = [];
            node = node.firstChild;
            while (node) {
                if (node.nodeType == 3)
                    result.push(node);
                if (node.nodeType == 1)
                    result = result.concat(getTextNodes(node, x, y));

                node = node.nextSibling;
            }

            return result;
        }

        var element = document.elementFromPoint(x, y);
        var nodes = getTextNodes(element, x, y);
        if (!nodes.length)
            return null;
        var node = nodes[0];

        var range = document.createRange();
        range.setStart(node, 0);
        range.setEnd(node, 1);

        for (var i = nodes.length; i--;) {
            var node = nodes[i],
                text = node.nodeValue;


            range = document.createRange();
            range.setStart(node, 0);
            range.setEnd(node, text.length);

            if (!inObject(x, y, range))
                continue;

            for (var j = text.length; j--;) {
                if (text.charCodeAt(j) <= 32)
                    continue;

                range = document.createRange();
                range.setStart(node, j);
                range.setEnd(node, j + 1);

                if (inObject(x, y, range)) {
                    range.setEnd(node, j);
                    return range;
                }
            }
        }

        return range;
    };
}
Run Code Online (Sandbox Code Playgroud)


Yan*_*roo 12

以下是我从像素位置获取文本节点内字符位置的调查结果:

  • 标准化方法:使用document.caretRangeFromPoint(x,y) 从位置获取范围请参阅W3c中的规范.这正是我所寻找的.问题是,在撰写本文时,Chrome是唯一实现此方法的Web浏览器(2010年7月)
  • MS IE方式使用专有的textRange.moveToPoint(x,y).
  • Firefox方式:如果从鼠标事件中检索像素位置(x,y),则Firefox将向事件对象添加两个有用的属性:rangParentrangeOffset
  • 对于Safari和Opera(实际上唯一的跨浏览器方法)是重新构成文本节点的包含框,然后使用包含框内的像素位置来推断字符位置.要做到这一点,你必须:
    1. 将所有文本节点包装到<span>元素中(维度信息仅适用于元素,不适用于文本节点)
    2. 调用span.getClientRects()以获取每个textNode的包含框(包装到<span>中).如果文本节点跨越多行,您将获得几个框.
    3. 找到包含(x,y)像素位置的框,并根据总像素宽度和文本长度,使用简单的"三规则"推断字符位置.


Jul*_*egg 5

在MSIE下,您写道:

var range = document.selection.createRange();
range.moveToPoint(x, y); 
Run Code Online (Sandbox Code Playgroud)

对于其他浏览器,我们的想法是确定x/y位置的HTML元素并在其上创建一个字符选择.基于此range.getBoundingClientRect(),您可以确定是否在x/y位置之前或之后选择一个字符.然后我们可以选择下一个字符,直到选择位置位于x/y位置.我为Firefox,Safari和Chrome编写了以下实现:

var nodeInfo = getSelectionNodeInfo(x, y);
var range = document.createRange();
range.setStart(nodeInfo.node, nodeInfo.offsetInsideNode);
range.setEnd(nodeInfo.node, nodeInfo.offsetInsideNode);

/**
Emulates MSIE function range.moveToPoint(x,y) b
returning the selection node info corresponding
to the given x/y location.

@param x the point X coordinate
@param y the point Y coordinate
@return the node and offset in characters as 
{node,offsetInsideNode} (e.g. can be passed to range.setStart) 
*/
function getSelectionNodeInfo(x, y) {
    var startRange = document.createRange();
    window.getSelection().removeAllRanges();
    window.getSelection().addRange(startRange);

    // Implementation note: range.setStart offset is
    // counted in number of child elements if any or
    // in characters if there is no childs. Since we
    // want to compute in number of chars, we need to
    // get the node which has no child.
    var elem = document.elementFromPoint(x, y);
    var startNode = (elem.childNodes.length>0?elem.childNodes[0]:elem);
    var startCharIndexCharacter = -1;
    do {
        startCharIndexCharacter++;
        startRange.setStart(startNode, startCharIndexCharacter);
        startRange.setEnd(startNode, startCharIndexCharacter+1);
        var rangeRect = startRange.getBoundingClientRect();
    } while (rangeRect.left<x && startCharIndexCharacter<startNode.length-1);

    return {node:startNode, offsetInsideNode:startCharIndexCharacter};
}
Run Code Online (Sandbox Code Playgroud)

这两段代码已经过测试:

  • MSIE 7,MSIE 9
  • Firefox 5,Firefox 10
  • Chrome 9
  • Safari 5

未测试以下情况:

  • 缩放因子问题
  • 包含多个文本行的HTML元素