javascripthtmlcsstextareacaret

Scrollable Textarea jumps on text cut


I have a textarea with these stylings:

textarea {
    width: 300px;
    min-height: 105px;
    max-height: 500px;
    overflow-y: auto;
    line-height: 20px;
}

When I select text and scroll down until I cant see where the selection started and I try to cut, the scroll just jumps around the bottom of the textarea. Same happens when I try to undo or redo text, basically it doesnt go back to where the caret position is.

Fixing this would be really helpful but even a code which finds the line where the caret is would be a big help since I can manage it with that. To find the caret line position is also hard since splitting the value with ('\n') wouldnt do the job, because there can be sentences which wrap into multiple lines and it would count them as one, so finding its line position can be hard too.

Thank u in advance and tell me if u need more info


Solution

  • Solving this problem does not require a lot of code but a few tricks. First here is the code that you need:

    <script>
        let textareas = document.querySelectorAll("textarea");
        textareas.forEach(textarea => {
            textarea.addEventListener("cut", () => jumpToCaret(textarea));
            textarea.addEventListener("input", function(e) {
                if (e.inputType == "historyUndo" || e.inputType == "historyRedo") {
                    jumpToCaret(textarea);
                }
            });
        });
        
        function jumpToCaret(textarea) {
            setTimeout(function() {
                textarea.blur();
                textarea.focus();
            }, 0);
        }
    </script>
    

    Add it to the end of your body tag or use a callback to make sure that the DOM has loaded.

    Now what is happening here?

    1. Find all textareas and loop them

      let textareas = document.querySelectorAll("textarea");
      textareas.forEach(textarea => {
      
    2. Add event listeners for cutting text and undo / redo. The latter is a bit tricky but this will work:

      textarea.addEventListener("cut", () => jumpToCaret(textarea));
      textarea.addEventListener("input", function(e) {
         if (e.inputType == "historyUndo" || e.inputType == "historyRedo") {
            jumpToCaret(textarea);
         }
      });
      
    3. Jump back to the caret: The trick is to blur and focus again. On focus browser will scroll to the caret automatically. But you have to put this into a setTimeout(func, 0). I will not go into detail with that. Check Why is setTimeout(fn, 0) sometimes useful? if you are interested in the JS event loop.

      function jumpToCaret(textarea) {
         setTimeout(function() {
            textarea.blur();
            textarea.focus();
         }, 0);
      }