按键事件发生后如何获取文本光标位置?
How to get text cursor position after keypress event happened?
我正在写一个语法荧光笔。荧光笔应在输入文本和使用箭头键导航时立即更新突出显示。
我面临的问题是,当触发 'keypress' 事件时,您仍然可以通过 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>
在示例中,将插入符号放在单词 'foo' 之前,然后按 →(右箭头键)。
在您最喜欢的 DevTool 的控制台中,您会看到以下内容:
keydown 0
keypress 0
keyup 1
除了 keypress
之外的 0
显然是旧的插入符号位置。如果你长按→,你会得到这样的结果:
keydown 0
keypress 0
keydown 1
keypress 1
keydown 1
keypress 1
keydown 2
keypress 2
keyup 2
我想要得到的是新的插入符号位置,就像我为 'keyup' 或 'input' 得到的一样。虽然 'keyup' 触发得太晚(我想在按下键时突出显示语法)并且 'input' 仅在实际有一些输入时触发(但 → 不产生任何输入)。
是否有一个事件在插入符号位置改变后触发,而不仅仅是在输入时触发?或者我是否必须计算文本光标的位置,如果是,如何计算? (我假设当文本换行并按下 ↓(向下箭头键)时,这会变得非常复杂。)
您可以使用 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>
该方法解决了密钥处理问题。在您的示例中,div
中还有一个 span
元素,它改变了
返回的位置值
window.getSelection().getRangeAt(0).startOffset
这是使用 '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
.
- 无法处理多行。
- 它不处理所有导航选项。例如。通过 Ctrl+→/← 逐字跳转无法识别。
而且可能还有更多我没有想到的问题。虽然可以处理所有这些问题,但它使实施变得非常复杂。因此,在 event for caret position changes.
之前,ConnorsFan 提供的简单 setTimeout(..., 0)
解决方案绝对更可取
有人刚刚在我的 request for an event for caret position changes that there is also a 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>
我正在写一个语法荧光笔。荧光笔应在输入文本和使用箭头键导航时立即更新突出显示。
我面临的问题是,当触发 'keypress' 事件时,您仍然可以通过 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>
在示例中,将插入符号放在单词 'foo' 之前,然后按 →(右箭头键)。
在您最喜欢的 DevTool 的控制台中,您会看到以下内容:
keydown 0
keypress 0
keyup 1
除了 keypress
之外的 0
显然是旧的插入符号位置。如果你长按→,你会得到这样的结果:
keydown 0
keypress 0
keydown 1
keypress 1
keydown 1
keypress 1
keydown 2
keypress 2
keyup 2
我想要得到的是新的插入符号位置,就像我为 'keyup' 或 'input' 得到的一样。虽然 'keyup' 触发得太晚(我想在按下键时突出显示语法)并且 'input' 仅在实际有一些输入时触发(但 → 不产生任何输入)。
是否有一个事件在插入符号位置改变后触发,而不仅仅是在输入时触发?或者我是否必须计算文本光标的位置,如果是,如何计算? (我假设当文本换行并按下 ↓(向下箭头键)时,这会变得非常复杂。)
您可以使用 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>
该方法解决了密钥处理问题。在您的示例中,div
中还有一个 span
元素,它改变了
window.getSelection().getRangeAt(0).startOffset
这是使用 '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
. - 无法处理多行。
- 它不处理所有导航选项。例如。通过 Ctrl+→/← 逐字跳转无法识别。
而且可能还有更多我没有想到的问题。虽然可以处理所有这些问题,但它使实施变得非常复杂。因此,在 event for caret position changes.
之前,ConnorsFan 提供的简单setTimeout(..., 0)
解决方案绝对更可取
有人刚刚在我的 request for an event for caret position changes that there is also a 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>