首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >JavaScript将鼠标位置转换为选择范围

JavaScript将鼠标位置转换为选择范围
EN

Stack Overflow用户
提问于 2011-11-15 05:34:48
回答 4查看 4K关注 0票数 18

我希望能够将当前鼠标位置转换为一个范围,特别是在CKEditor中。

CKEditor提供了根据范围设置光标的接口:

代码语言:javascript
运行
复制
var ranges = new CKEDITOR.dom.range( editor.document );
editor.getSelection().selectRanges( [ ranges ] );

由于CKEditor提供了此API,因此可以通过删除此要求来简化问题,只需找到一种方法来从包含各种HTML元素的div上的鼠标坐标生成范围。

然而,这与将鼠标坐标转换为文本区域中的光标位置不同,因为文本区域具有固定的列宽和行高,其中CKEditor通过iframe呈现HTML。

基于this,该范围看起来可以应用于元素。

您如何计算出最接近当前鼠标位置的开始/结束范围?

Edit:如何使用ckeditor API选择mouseup事件的范围的示例。

代码语言:javascript
运行
复制
editor.document.on('mouseup', function(e) {
    this.focus();
    var node = e.data.$.target;

    var range = new CKEDITOR.dom.range( this.document );
    range.setStart(new CKEDITOR.dom.node(node), 0);
    range.collapse();

    var ranges = [];
    ranges.push(range);
    this.getSelection().selectRanges( ranges );
});

上述示例的问题在于,事件目标节点(e.data.$.target)仅为HTML、BODY或IMG等节点触发,而不是为文本节点触发。即使有,这些节点也表示文本块,不支持将光标设置到该文本块中鼠标的位置。

EN

回答 4

Stack Overflow用户

发布于 2012-04-15 23:16:27

在浏览器中,你想要做的事情真的很难。我对ckeditor不是特别熟悉,但是常规的javascript允许你使用一个范围来选择文本,所以我不认为它添加了什么特别的东西。您必须找到包含单击的浏览器元素,然后在单击的元素中找到字符。

检测浏览器元素很容易:您需要在每个元素上注册处理程序,或者使用事件的目标字段。有很多关于这方面的信息,如果这是你遇到问题的原因,可以在stackoverflow上问一个更具体的问题。

有了元素后,您需要找出元素中的哪个字符被单击,然后创建一个适当的范围来将光标放在那里。正如你链接到的帖子所说,浏览器的变化使得这一点变得非常困难。这个页面有点过时,但对范围进行了很好的讨论:http://www.quirksmode.org/dom/range_intro.html

Range不能告诉您它们在页面上的位置,因此您必须使用另一种技术来找出单击了哪一段文本。

我从来没有在javascript中看到过一个完整的解决方案。几年前,我做了一个,但我没有想出一个令我满意的答案(一些非常困难的情况)。我使用的方法是一个可怕的技巧:在文本中插入跨度,然后使用它们执行二进制搜索,直到找到包含鼠标单击的尽可能小的跨度。跨度不会改变布局,所以你可以使用跨度的position_x/y属性来找出它们包含点击。

例如,假设在一个节点中有以下文本:

代码语言:javascript
运行
复制
<p>Here is some paragraph text.</p>

我们知道点击是在这一段的某处。用跨度将段落一分为二:

代码语言:javascript
运行
复制
<p><span>Here is some p</span>aragraph text.</p>

如果跨度包含点击坐标,则继续在该半部分中进行二进制搜索,否则搜索后半部分。

这对于单行非常有效,但如果文本跨越多行,则必须首先查找换行,否则跨度可能会重叠。你还必须弄清楚当点击不是在任何文本上而是在元素中时该怎么做-例如,超过段落中最后一行的结尾。

自从我在这方面工作以来,浏览器变得更快了。它们现在可能足够快了,可以在每个字符周围添加s,然后在每个字符周围添加s,等等,以创建易于搜索的二叉树。您可以尝试这种方法-它将使您更容易确定您正在处理的是哪一行。

这真的是一个很难的问题,如果有答案,可能不值得你花时间去想。

票数 2
EN

Stack Overflow用户

发布于 2012-03-28 11:20:45

有两种方法可以做到这一点,就像每个WYSIWYG一样。

首先:-你放弃了,因为它太难了,最终会成为浏览器杀手;

第二:-你试图解析文本并将其放在半透明的textarea或div中的确切位置,但这里有两个问题:

1)如何解析动态数据块以仅获取文本,并确保将其映射到实际内容的确切位置

2)如何解决更新以解析您在编辑器中键入的每个该死的字符或执行的每个操作。

归根结底,这只是一次“DOM树阴暗面的残酷冒险”,但如果你选择第二种方式,那么你的文章中的代码将会像魔咒一样工作。

票数 1
EN

Stack Overflow用户

发布于 2020-09-23 00:59:53

很抱歉撞到了一个老帖子,但我想在这里张贴这篇文章,以防其他人偶然发现这个问题,因为关于这个问题的信息很少。我只需要为Outlook for web用户脚本编写一个函数来做这件事,因为它们覆盖了默认的拖放功能,并在撰写框中中断了它。这是我想出的解决方案:

代码语言:javascript
运行
复制
function rangeFromCoord(x, y) {
    const closest = {
        offset: 0,
        xDistance: Infinity,
        yDistance: Infinity,
    };

    const {
        minOffset,
        maxOffset,
        element,
    } = (() => {
        const range = document.createRange();
        range.selectNodeContents(document.elementFromPoint(x, y));
        return {
            element: range.startContainer,
            minOffset: range.startOffset,
            maxOffset: range.endOffset,
        };
    })();

    for(let i = minOffset; i <= maxOffset; i++) {
        const range = document.createRange();
        range.setStart(element, i);
        range.setEnd(element, i);
        const marker = document.createElement("span");
        marker.style.width = "0";
        marker.style.height = "0";
        marker.style.position = "absolute";
        marker.style.overflow = "hidden";
        range.insertNode(marker);
        const rect = marker.getBoundingClientRect();
        const distX = Math.abs(x - rect.left);
        const distY = Math.abs(y - rect.top);
        marker.remove();
        if(closest.yDistance > distY) {
            closest.offset = i;
            closest.xDistance = distX;
            closest.yDistance = distY;
        } else if(closest.yDistance === distY) {
            if(closest.xDistance > distX) {
                closest.offset = i;
                closest.xDistance = distX;
                closest.yDistance = distY;
            }
        }
    }

    const range = document.createRange();
    range.setStart(element, closest.offset);
    range.setEnd(element, closest.offset);
    return range;
}

您所要做的就是传入工作区坐标,该函数将自动选择该位置上最具体的元素。它将使用该选择来获取浏览器使用的父元素(最明显的是contenteditable元素),以及最大和最小偏移量。然后,它将继续迭代偏移量,在每个偏移量上放置带有position: absolute; width: 0; height: 0; overflow: hidden;marker span元素,以探测它们的位置,删除它们,并检查距离。根据大多数文本编辑器,它将首先在Y坐标上尽可能接近,然后在X坐标上移动。一旦它找到最近的位置,它将创建一个新的选择并返回它。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/8128469

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档