我正在写一个语法荧光笔。突出显示器应在输入文本并使用箭头键导航时立即更新突出显示。
我面临的问题是当'按键'触发事件后,您仍然可以通过window.getSelection()
获取文本光标的旧位置。
示例:
function handleKeyEvent(evt) {
console.log(evt.type, window.getSelection().getRangeAt(0).startOffset);
}
var div = document.querySelector("div");
div.addEventListener("keydown", handleKeyEvent);
div.addEventListener("keypress", handleKeyEvent);
div.addEventListener("input", handleKeyEvent);
div.addEventListener("keyup", handleKeyEvent);

<div contenteditable="true">f<span class="highlight">oo</span></div>
&#13;
在示例中,将插入符号放在单词&#39; foo&#39;之前,然后按→(右箭头键)。
在您最喜爱的DevTool的控制台中,您将看到以下内容:
keydown 0
keypress 0
keyup 1
除了0
之外keypress
显然是旧的插入位置。如果再按住→,你会得到类似的结果:
keydown 0
keypress 0
keydown 1
keypress 1
keydown 1
keypress 1
keydown 2
keypress 2
keyup 2
我想得到的是新的插入位置,就像我想要的那样#keyup&#39;或者&#39;输入&#39;。虽然&#39; keyup&#39;被解雇太晚了(我想在按下键的同时突出显示语法)并且输入&#39;只有在实际输入时才触发(但→不会产生任何输入)。
是否有一个事件在插入符号位置发生变化后触发而不仅仅是输入?或者我是否必须计算文本光标的位置,如果是,如何计算? (我假设当文本换行并按↓(向下箭头键)时,这会变得非常复杂。)
答案 0 :(得分:10)
您可以使用setTimeout
异步处理keydown
事件:
function handleKeyEvent(evt) {
setTimeout(function () {
console.log(evt.type, window.getSelection().getRangeAt(0).startOffset);
}, 0);
}
var div = document.querySelector("div");
div.addEventListener("keydown", handleKeyEvent);
<div contenteditable="true">This is some text</div>
该方法解决了关键处理问题。在您的示例中,span
内部还有一个div
元素,它会改变
window.getSelection().getRangeAt(0).startOffset
答案 1 :(得分:1)
这是使用'keydown'事件纠正位置的解决方案:
function handleKeyEvent(evt) {
var caretPos = window.getSelection().getRangeAt(0).startOffset;
if (evt.type === "keydown") {
switch(evt.key) {
case "ArrowRight":
if (caretPos < evt.target.innerText.length - 1) {
caretPos++;
}
break;
case "ArrowLeft":
if (caretPos > 0) {
caretPos--;
}
break;
case "ArrowUp":
case "Home":
caretPos = 0;
break;
case "ArrowDown":
case "End":
caretPos = evt.target.innerText.length;
break;
default:
return;
}
}
console.log(caretPos);
}
var div = document.querySelector("div");
div.addEventListener("keydown", handleKeyEvent);
div.addEventListener("input", handleKeyEvent);
<div contenteditable="true">f<span class="highlight">oo</span></div>
不幸的是,这个解决方案存在一些缺陷:
<span>
之类的子元素内部时,它不会提供正确的startOffset
或正确的startContainer
。可能还有更多我没想过的问题。虽然可以处理所有这些问题,但它使实现变得非常复杂。因此,ConnorsFan提供的简单setTimeout(..., 0)
解决方案绝对是可取的,直到有event for caret position changes。
答案 2 :(得分:0)
有人在我的request for an event for caret position changes上提到,还有一个selectionchange
event,每次选择更改时都会在文档上触发。
然后,通过调用window.getSelection()
,可以获取正确的光标位置。
示例:
function handleSelectionChange(evt) {
console.log(evt.type, window.getSelection().getRangeAt(0));
}
document.addEventListener("selectionchange", handleSelectionChange);
<div contenteditable="true">f<span class="highlight">oo</span></div>