浏览器页面中选定文本的坐标

IT技术 javascript html cursor selection textselection
2021-01-19 14:54:03

我需要文本选择开头的像素坐标(页面上的任何位置,而不是文本区域中)。

我尝试使用光标坐标,但效果不佳,因为光标坐标和选择的开头并不总是相同的(例如,当用户拖动文本时)。

我希望有人有解决方案!

3个回答

在IE> = 9和非IE浏览器(Firefox 4+,WebKit浏览器自2009年年初发布,歌剧11,也许更早),你可以使用getClientRects()的方法Range在 IE 4 - 10 中,您可以使用可以从选择中提取boundingLeftboundingTop属性TextRange这是一个功能,可以在最近的浏览器中执行您想要的操作。

请注意,在某些情况下,您可能会错误地获得坐标0, 0,如@Louis 的评论中所述。在这种情况下,您将不得不退回到临时插入元素并获取其位置的解决方法。

jsFiddle:http : //jsfiddle.net/NFJ9r/132/

代码:

function getSelectionCoords(win) {
    win = win || window;
    var doc = win.document;
    var sel = doc.selection, range, rects, rect;
    var x = 0, y = 0;
    if (sel) {
        if (sel.type != "Control") {
            range = sel.createRange();
            range.collapse(true);
            x = range.boundingLeft;
            y = range.boundingTop;
        }
    } else if (win.getSelection) {
        sel = win.getSelection();
        if (sel.rangeCount) {
            range = sel.getRangeAt(0).cloneRange();
            if (range.getClientRects) {
                range.collapse(true);
                rects = range.getClientRects();
                if (rects.length > 0) {
                    rect = rects[0];
                }
                x = rect.left;
                y = rect.top;
            }
            // Fall back to inserting a temporary element
            if (x == 0 && y == 0) {
                var span = doc.createElement("span");
                if (span.getClientRects) {
                    // Ensure span has dimensions and position by
                    // adding a zero-width space character
                    span.appendChild( doc.createTextNode("\u200b") );
                    range.insertNode(span);
                    rect = span.getClientRects()[0];
                    x = rect.left;
                    y = rect.top;
                    var spanParent = span.parentNode;
                    spanParent.removeChild(span);

                    // Glue any broken text nodes back together
                    spanParent.normalize();
                }
            }
        }
    }
    return { x: x, y: y };
}

更新

作为评论的结果,我提交了一个 WebKit 错误,现在已修复。

https://bugs.webkit.org/show_bug.cgi?id=65324

@Bouke:啊,说得对。我已经按照您的要求更新了我的答案,方法是在开始时将范围折叠到一个点,然后再获得它的位置。
2021-03-13 14:54:03
@Mr_Green:此代码特定于常规 HTML 内容中的选择/范围,而不是文本区域,Rangy 也是如此。我不相信有可能创建一个通用的跨浏览器解决方案来获取 textarea 光标的坐标而不进行大量繁琐的文本测量,这就是为什么我没有回答你的另一个问题。
2021-03-19 14:54:03
我试过这个代码。x: 0, y:0即使在 Firefox 20.0.1 中移动插入符号位置,它也总是返回请看看我最近问的这个类似的问题我正在寻找适用于 IE8+、chrome 和 firefox 的跨浏览器解决方案。
2021-03-28 14:54:03
在单行选择上完美运行,但是当您选择多行(从第一行的一半位置开始)时,它会显示第一个选择行开头的坐标,而不是它自己选择的开头...我知道这可能是因为 getBounding 函数,但有什么方法可以改变它吗?
2021-04-02 14:54:03
@TimDown 恐怕文本区域的问题不止于此。在 Chrome 中运行此小提琴并单击“此处”(粗体)左侧,而不选择文本。你会得到0, 0在 Chrome 中更容易重现,但基本问题是,如果零宽度范围不在文本节点中,则矩形将具有0, 0坐标。我无法通过简单的鼠标单击在 FF 中重现它,因为它更喜欢在单击时在文本节点内设置插入符号,但如果以编程方式操作选择,它也可能在 FF 中发生。
2021-04-06 14:54:03

如果插入符号位于空元素中,则 TimDown 的上述答案不起作用。

下面的代码解决了这个问题。请注意它与 TimDown 的解决方案几乎相同,除了此代码在调用之前检查range.getClientRects()数组length>0range.getClientRects()[0]

function getSelectionCoords() {
    var sel = document.selection, range, rect;
    var x = 0, y = 0;
    if (sel) {
        if (sel.type != "Control") {
            range = sel.createRange();
            range.collapse(true);
            x = range.boundingLeft;
            y = range.boundingTop;
        }
    } else if (window.getSelection) {
        sel = window.getSelection();
        if (sel.rangeCount) {
            range = sel.getRangeAt(0).cloneRange();
            if (range.getClientRects) {
                range.collapse(true);
                if (range.getClientRects().length>0){
                    rect = range.getClientRects()[0];
                    x = rect.left;
                    y = rect.top;
                }
            }
            // Fall back to inserting a temporary element
            if (x == 0 && y == 0) {
                var span = document.createElement("span");
                if (span.getClientRects) {
                    // Ensure span has dimensions and position by
                    // adding a zero-width space character
                    span.appendChild( document.createTextNode("\u200b") );
                    range.insertNode(span);
                    rect = span.getClientRects()[0];
                    x = rect.left;
                    y = rect.top;
                    var spanParent = span.parentNode;
                    spanParent.removeChild(span);

                    // Glue any broken text nodes back together
                    spanParent.normalize();
                }
            }
        }
    }
    return { x: x, y: y };
}
@TimDown 当然,您可以将其包含在答案中。
2021-03-24 14:54:03
好点子。你介意我在答案中包含那个检查吗?
2021-04-09 14:54:03

下面的代码是 Tim Down 给出的解决方案的简化和现代化版本。它还使用了更兼容浏览器的选择 APIwindow.getSelection()而不是window.document.selection

type Coord = {
  x: number;
  y: number;
};

// atStart: if true, returns coord of the beginning of the selection,
//          if false, returns coord of the end of the selection
function getSelectionCoords(atStart: boolean): Coord | null {
  const sel = window.getSelection();

  // check if selection exists
  if (!sel.rangeCount) return null;

  // get range
  let range = sel.getRangeAt(0).cloneRange();
  if (!range.getClientRects) return null;

  // get client rect
  range.collapse(atStart);
  let rects = range.getClientRects();
  if (rects.length <= 0) return null;

  // return coord
  let rect = rects[0];
  return { x: rect.x, y: rect.y };
}