JavaScript 将鼠标位置转换为选择范围
我希望能够将当前鼠标位置转换为一个范围,尤其是在 CKEditor 中.
I would like to be able to convert the current mouse position to a range, in CKEditor in particular.
CKEditor 提供了根据范围设置光标的 API:
The CKEditor provides an API for setting the cursor according to a range:
var ranges = new CKEDITOR.dom.range( editor.document );
editor.getSelection().selectRanges( [ ranges ] );
由于 CKEditor 提供了这个 API,这个问题可以通过删除这个要求来简化,只需找到一种方法来生成从鼠标坐标到包含各种 HTML 元素的 div 的范围.
Since CKEditor provides this API, the problem may be simplified by removing this requirement and just find a way to produce the range from the mouse coordinates over a div containing various HTML elements.
但是,这与将鼠标坐标转换为文本区域中的光标位置不同,因为文本区域具有固定的列宽和行高,CKEditor 通过 iframe 呈现 HTML.
However, this is not the same as converting a mouse coordinate into the cursor position in a textarea since textareas have fixed column widths and row heights where the CKEditor renders HTML through an iframe.
基于 this,它看起来像范围可以应用于元素.
Based on this, it looks like the range may be applied to elements.
您如何确定最接近当前鼠标位置的开始/结束范围?
How would you figure out the start/end range which is closest to the current mouse position?
一个如何使用 ckeditor API 来选择 mouseup 事件范围的示例.
An example of how one might use the ckeditor API to select a range on the mouseup event.
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等节点触发,而不针对文本节点触发.即使是这样,这些节点也代表不支持将光标设置为鼠标在该文本块中的位置的文本块.
The problem with the above example is that the event target node (e.data.$.target) is only firing for nodes such as HTML, BODY, or IMG but not for text nodes. Even if it did, these nodes represent chunks of text which wouldn't support setting the cursor to the position of the mouse within that chunk of text.
推荐答案
在浏览器中你要做的事情真的很难.我特别不熟悉ckeditor,但常规javascript允许您使用范围选择文本,所以我认为它不会添加任何特别的东西.您必须找到包含点击的浏览器元素,然后在被点击的元素中找到字符.
What you're trying to do is really hard in a browser. I'm not familar with ckeditor in particular, but regular javascript allows you to select text using a range so I don't think it's adding anything special. You have to find the browser element that contains the click, then find the character within the element that was clicked.
检测浏览器元素很简单:您需要在每个元素上注册处理程序,或者使用事件的目标字段.有很多关于这方面的信息,如果您遇到问题,请在 stackoverflow 上提出更具体的问题.
Detecting the browser element is the easy bit: you need to either register your handler on every element, or use the event's target field. There is lots of info on this out there, ask a more specific question on stackoverflow if that's what you're having trouble with.
一旦你有了元素,你需要找出元素中的哪个字符被点击了,然后创建一个合适的范围来把光标放在那里.正如您链接到的帖子所述,浏览器变体使这变得非常困难.此页面有点过时,但对范围进行了很好的讨论:http://www.quirksmode.org/dom/range_intro.html
Once you have the element you need to find out which character within the element was clicked, then create an appropriate range to put the cursor there. As the post you linked to stated, browser variations make this really hard. This page is a bit dated, but has a good discussion of ranges: http://www.quirksmode.org/dom/range_intro.html
范围无法告诉您它们在页面上的位置,因此您必须使用另一种技术来找出点击了哪些文本.
Ranges can't tell you their positions on the page, so you'll have to use another technique to find out what bit of text was clicked.
我从未在 javascript 中看到过完整的解决方案.几年前,我研究过一个,但我没有想出一个我满意的答案(一些非常困难的边缘案例).我使用的方法是一种可怕的 hack:在文本中插入 span,然后使用它们执行二进制搜索,直到找到包含鼠标单击的最小 span.Span 不会更改布局,因此您可以使用 Span 的 position_x/y 属性来找出它们是否包含点击.
I've never seen a complete solution to this in javascript. A few years ago I worked on one but I didn't come up with an answer I was happy with (some really hard edge cases). The approach I used was a horrible hack: insert spans into the text then use them to perform binary search until you find the smallest possible span containing the mouse click. Spans don't change the layout, so you can use the span's position_x/y properties to find out they contain the click.
例如假设您在节点中有以下文本:
E.g. suppose you have the following text in a node:
<p>Here is some paragraph text.</p>
我们知道点击发生在本段的某处.用 span 将段落分成两半:
We know the click was somewhere in this paragraph. Split the paragraph in half with a span:
<p><span>Here is some p</span>aragraph text.</p>
如果span包含点击坐标,则继续二分查找,否则查找下半部分.
If the span contains the click coordinates, continue binary search in that half, otherwise search the second half.
这对单行非常有效,但如果文本跨越多行,则必须首先找到换行符,否则跨度可能会重叠.您还必须弄清楚当点击不是在任何文本上而是在元素中时该怎么做——例如,超过段落中最后一行的结尾.
This works great for single lines, but if the text spans multiple lines you have to first find line breaks, or the spans can overlap. You also have to work out what to do when the click wasn't on any text but was in the element --- past the end of the last line in a paragraph for example.
自从我从事这个工作以来,浏览器的速度已经快了很多.它们现在可能足够快,可以在每个字符周围添加 s,然后在每两个字符周围添加等以创建易于搜索的二叉树.您可以尝试这种方法 - 它会让您更容易确定您正在处理哪条线路.
Since I worked on this browsers have got a lot faster. They're probably fast enough now to add s around each character, then around each two characters etc to create a binary tree which is easy to search. You could try this approach - it would make it much easier to work out which line you're working on.
TL;DR 这是一个非常困难的问题,如果有答案,可能不值得你花时间想出它.
TL;DR this is a really hard problem and if there is an answer, it might not be worth your time to come up with it.
相关文章