按键事件发生后如何获取文本光标位置?

时间:2016-05-02 08:32:07

标签: javascript dom javascript-events keyboard-events

我正在写一个语法荧光笔。突出显示器应在输入文本并使用箭头键导航时立即更新突出显示。

我面临的问题是当'按键'触发事件后,您仍然可以通过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;
&#13;
&#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;只有在实际输入时才触发(但不会产生任何输入)。

是否有一个事件在插入符号位置发生变化后触发而不仅仅是输入?或者我是否必须计算文本光标的位置,如果是,如何计算? (我假设当文本换行并按(向下箭头键)时,这会变得非常复杂。)

3 个答案:

答案 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
  • 无法处理多行。
  • 它不处理所有导航选项。例如。通过 Ctrl + / 无法识别跳字。

可能还有更多我没想过的问题。虽然可以处理所有这些问题,但它使实现变得非常复杂。因此,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>