Как отслеживать курсор/курсор в contenteditable?
Я хотел бы отслеживать перемещение курсора/курсора в контентной форме. Я не уверен, что лучший способ сделать это.
В настоящее время я слушаю клики, keydown, keyup. (клавиатура, конечно, даже не срабатывает для таких вещей, как клавиши со стрелками или ctrl-x.)
В то время как щелчок работает нормально, проблема с keydown заключается в том, что он запускается до того, как каретка действительно перемещается, поэтому, когда я запрашиваю текущий диапазон выбора документа, я получаю старую позицию, а не новую. Но если я полагаюсь на клавиатуру, чтобы получить обновленную позицию, она срабатывает слишком поздно: каретка перемещается сразу же после нажатия клавиши, но клавиша отпускается произвольным временем позже.
Это должно быть возможно, потому что такие вещи, как CKeditor, могут это сделать. Любые подсказки?
Ответы
Ответ 1
Приятно читать, что люди говорят о CKEditor:). Я один из его разработчиков, и я не много работал над выбором, но я постараюсь помочь.
Я знаю, что у нас есть внутреннее событие selectionChange
. Итак, когда мы проверяем, изменилось ли это?...... по крайней мере один раз за каждые 200 мс. См.:
http://dev.ckeditor.com/browser/CKEditor/trunk/_source/plugins/selection/plugin.js#L39
Мы также проверяем выбор каждый раз, когда знаем, что его можно было изменить (например, API или на клавиатуре/мыши или на собственном выборе - http://dev.ckeditor.com/browser/CKEditor/trunk/_source/plugins/selection/plugin.js#L554). Итак... в значительной степени все время:) AFAIK мы делаем некоторые трюки, поэтому он не сжигает ваш процессор, но он все еще тяжелый. Однако, если мы это сделаем, тогда это единственный возможный способ, чтобы это работало так хорошо.
К сожалению, обработка выбора на сегодняшний день является наихудшей задачей в мире wysiwygs. Он так разбит во всех - старых и новых браузерах. Более 75% LOC в файле, который я связал выше, - это хаки и трюки. Это полное безумие.
Ответ 2
В Mozilla и Opera неприятный бизнес обработки ключевых событий и событий мыши - ваш единственный вариант. Это не только неудобно, но и не охватывает каждый случай: можно изменить выбор с помощью меню редактирования и контекста (например, с помощью Select All). Чтобы покрыть это, вам также нужно добавить какой-то опрос объекта выделения.
Тем не менее, в IE (вплоть до 5,5) и в недавнем ISK WebKit существует selectionchange
событие, которое срабатывает в документе при каждом изменении выбора.
document.onselectionchange = function() {
alert("Selection changed!");
};
Есть вероятность, что Mozilla будет поддерживать его в будущем: https://bugzilla.mozilla.org/show_bug.cgi?id=571294
Ответ 3
Это нелегкая задача по причинам, которые вы сказали. Я придумал какой-то клоун вроде этого:
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 вы должны поймать событие cut
и paste
, и это на самом деле другая боль в bollocks.
В конце концов, я решил, что это не стоит усилий для моих целей. Возможно, у вас разные потребности.
Ответ 4
WRT ловит событие после обновления: я просто переношу свои функции обработчика в таймауты:
editor.onkeydown = function() {
window.setTimeout( function(){
// Your handler code here
}, 0 );
};
Это регистрирует ваш обработчик для выполнения в цикле событий браузера как можно скорее, но после обработки текущего события (например, щелчка). Но имейте в виду возможные расы, если у вас есть другие скрипты, изменяющие контент; нет гарантии, что ваш тайм-аут будет следующим в очереди.