19

我想在 contenteditable 中跟踪插入符号/光标的移动。不过,我不确定最好的方法是什么。

我目前正在听点击,keydown,keyup。(当然,按键甚至不会触发箭头键或 ctrl-x 之类的东西。)

虽然 click 工作正常,但 keydown 的问题是它在插入符号实际移动之前被触发,所以当我查询当前文档选择范围时,我得到的是旧位置而不是新位置。但是,如果我依靠 keyup 来获取更新的位置,它就会触发得太晚:一旦按下键,插入符号就会移动,但键会在任意时间之后释放。

这一定是可能的,因为像 CKeditor 这样的东西能够做到这一点。有什么提示吗?

4

4 回答 4

10

很高兴看到人们在谈论 CKEditor :)。我是它的开发人员之一,我在选择方面没有做太多工作,但我会尽力提供帮助。

我所知道的是我们有一个内部selectionChange事件。那么我们什么时候检查它是否改变了呢?... ...每 200 毫秒至少一次 :) 见:

http://dev.ckeditor.com/browser/CKEditor/trunk/_source/plugins/selection/plugin.js#L39

每次我们知道选择可能已被更改时,我们也会检查它(例如,通过 API 或在 keyup/mouseup 或在本机 selectionchange 上 - http://dev.ckeditor.com/browser/CKEditor/trunk/_source/plugins/selection /plugin.js#L554)。所以......几乎所有时间:) AFAIK 我们做了一些技巧,所以它不会烧毁你的 CPU,但它仍然很重。但是,如果我们这样做了,那么它是唯一可能的方法让它工作得这么好。

不幸的是,选择处理是迄今为止所见即所得世界中最糟糕的任务。它在所有旧浏览器和新浏览器中都被破坏了。我上面链接的文件中超过 75% 的 LOC 是黑客和技巧。那是完全的疯狂。

于 2012-07-13T22:31:57.653 回答
5

在 Mozilla 和 Opera 中,处理按键和鼠标事件是您唯一的选择。它不仅繁琐,而且并不涵盖所有情况:可以通过编辑和上下文菜单(例如,通过全选)更改选择。为了解决这个问题,您还需要添加某种对选择对象的轮询。

然而,在 IE(至少回到 5.5)和最近的 WebKit 中,只要选择更改,就会在文档上触发一个selectionchange事件。

document.onselectionchange = function() {
    alert("Selection changed!");
};

Mozilla 将来有机会支持它:https ://bugzilla.mozilla.org/show_bug.cgi?id=571294

于 2012-07-12T00:34:47.187 回答
0

由于你所说的原因,这不是一件容易的事。我想出了一些像这样的kludge:

var caretInterval, caretOffset;
document.addEventListener("keydown", function(e) {
    if (!e.target.contentEditable || caretInterval) return;
    if (e.keyCode !== 37 && e.keyCode !== 39) // Left and right
        return;
    var sel = getSelection();
    caretInterval = setInterval(function() {
        if (sel.type === "Caret") caretOffset = sel.baseOffset;
    }, 50);
});
document.addEventListener("keyup", function(e) {
    if (e.keyCode !== 37 && e.keyCode !== 39) // Left and right
        return;
    clearInterval(caretInterval);
    caretInverval = null;
    var sel = getSelection();
    if (sel.type === "Caret") caretOffset = sel.baseOffset;
});

如果有人试图同时按下左键和右键,可能会出现一个小问题。对于 ctrl-X 和 ctrl-V,你应该抓住cutandpaste事件,这实际上是胡说八道的另一个痛苦。

最后,我决定不值得为我的目的而付出努力。也许你有不同的需求。

于 2012-07-11T22:49:25.000 回答
0

WRT 在更新选择后捕获事件:我只是将处理程序函数包装在超时中:

editor.onkeydown = function() {
  window.setTimeout( function(){
    // Your handler code here
  }, 0 );
};

这会将您的处理程序注册为尽快在浏览器的事件循环中执行,但要在处理当前(例如单击)事件之后。但是,如果您有其他脚本修改内容,请注意可能的比赛;不能保证您的超时是下一个要运行的。

于 2012-07-26T13:06:22.880 回答