代码之家  ›  专栏  ›  技术社区  ›  Sebastian Zartner Emmanouil Chountasis

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

  •  27
  • Sebastian Zartner Emmanouil Chountasis  · 技术社区  · 9 年前

    我正在写语法高亮。在输入文本和使用箭头键导航时,荧光笔应立即更新突出显示。

    我面临的问题是,当“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
    

    那个 0 此外 keypress 显然是旧的插入符号位置。如果你坚持下去 再长一点,你会得到这样的结果:

    keydown 0
    keypress 0
    keydown 1
    keypress 1
    keydown 1
    keypress 1
    keydown 2
    keypress 2
    keyup 2
    

    我想得到的是新的插入符号位置,就像我在“keyup”或“input”中得到的位置一样。虽然“keyup”触发得太晚(我想在按下键时突出显示语法),而“input”仅在实际有一些输入时触发(但是 不会产生任何输入)。

    是否存在在插入符号位置更改后触发的事件,而不仅仅是在输入时触发的事件?或者我必须计算文本光标的位置,如果是,如何计算?(我认为当文本换行并按 (向下箭头键)

    3 回复  |  直到 5 年前
        1
  •  22
  •   ConnorsFan    8 年前

    你可以使用 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
    
        2
  •  2
  •   Sebastian Zartner Emmanouil Chountasis    8 年前

    以下是使用“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 .
    • 它不能处理多行。
    • 它不能处理所有导航选项。E、 g.通过跳转单词 Ctrl键 + / 无法识别。

    可能还有更多我没有想到的问题。虽然有可能处理所有这些问题,但这使得实现非常复杂。所以简单的 setTimeout(..., 0) ConnorsFan提供的解决方案在 event for caret position changes .

        3
  •  1
  •   Sebastian Zartner Emmanouil Chountasis    4 年前

    有人刚刚在我的 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>