How to get text cursor position after keypress event happened?

前端 未结 3 1636
悲哀的现实
悲哀的现实 2021-01-17 07:58

I am writing a syntax highlighter. The highlighter should update the highlighting immediately while entering text and navigating with the arrow keys.

The problem I\'

相关标签:
3条回答
  • 2021-01-17 08:14

    You can use setTimeout to process the keydown event asynchronously:

    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>

    That method addresses the key processing problem. In your example, you also have a span element inside of the div, which alters the position value returned by

    window.getSelection().getRangeAt(0).startOffset
    
    0 讨论(0)
  • 2021-01-17 08:22

    Someone just mentioned on my request for an event for caret position changes that there is also a selectionchange event, which is fired at the document everytime the selection has changed.

    This then allows to get the correct cursor position by calling window.getSelection().

    Example:

    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>

    0 讨论(0)
  • 2021-01-17 08:32

    Here's a solution correcting the position using the 'keydown' event:

    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>

    Unfortunately this solution as is has several flaws:

    • When inside a child element like the <span> in the example, it doesn't provide the correct startOffset nor the correct startContainer.
    • It cannot handle multiple lines.
    • It doesn't handle all navigation options. E.g. jumping word-wise via Ctrl+/ is not recognized.

    And there are probably more issues I didn't think of. While it would be possible to handle all those issues, it makes the implementation very complex. So the simple setTimeout(..., 0) solution provided by ConnorsFan is definitely preferable until there is an event for caret position changes.

    0 讨论(0)
提交回复
热议问题