Home > database >  Calling focus on a textarea breaks document.getSelection()
Calling focus on a textarea breaks document.getSelection()

Time:01-29

I'm currently trying to build an enhanced text input component for my Svelte app that will make it easy for users to input chemical formulas/equations, but when trying to create code that will move the cursor to the right position when the text input is clicked on, I'm running into issues.

My current HTML is laid out similar to the following:

<div  contenteditable>
    <span > /* text left of the cursor goes here */  </span>
    <span ></span>
    <span > /* text right of the cursor goes here */ </span>
</div>
<textarea ></textarea>

I'm using a textarea to watch for input and keydown events and using the information from those events to insert the proper text/special characters into the input-field div. The div is contenteditable because I want to be able to see where the user is trying to place their caret, shift real caret focus into the textarea, and move my simulated caret into the appropriate place.

The issue appears when attempting to retrieve the cursor position from the input-field div before shifting focus to the textarea. If there is ANY code present anywhere that attempts to shift focus into the textarea at any time, then the result returned by getSelection() will be broken. This happens if textarea.focus() is done synchronously, asynchronously using promises, asynchronously using setTimeout, or even placed in a different function that runs at a completely different time (triggered by a different event from the user). Removing this focus call makes the getSelection result correct again. This issue occurs with both textarea and input elements.

The result returned by getSelection is broken in either one of two ways:

  1. The anchor/focus/base/extent elements are all either the body element or the #svelte element (div that wraps all content in the page). This is not what the result should be, it should be either .left or .right. Offset numbers are also all incorrect.
  2. The result directly returned by getSelection is correct, but accessing any properties of the result does not give the correct values. This one is very strange, and I can't imagine why the values would be different in between lines of code, or depend on lines of code that haven't even been executed yet. Below is an image of the output when selection and selection.anchorNode are logged one after another.

Result of logging selection and selection.anchorNode

This behavior is very strange and I'm having trouble finding workarounds to these huge inconsistencies. Any help in explaining what's going on would be much appreciated. Below is a test page I made to demonstrate this bug.

<!DOCTYPE html>
<html>
    <head>
        <script>
            function handleFocus() {
                let textarea = document.getElementById('textarea')
                console.dir(document.getSelection())
                setTimeout(() => {
                    textarea.focus()
                })
            }
        </script>
        <style>
            
        </style>
    </head>
    <body>
        <div contenteditable tabindex="1" onfocus="handleFocus()">This is some sample text, click on me</div>
        <textarea id="textarea" tabindex="1"></textarea>
    </body>
</html>

CodePudding user response:

Since getSelection will only be correct after the event, you will have to call getSelection not in the handler but afterwards. You can do that from the handler by calling it in setTimeout as this will put it to the very end of the event loop. This would be a working solution for svelte:

<script>
    import {tick} from 'svelte'
    let textarea;
    let div;
    
    function handleFocus() {
        setTimeout(() => {
            const focusOffset = window.getSelection().focusOffset
            console.log(focusOffset)
            textarea.focus()
        }, 0)
  }
</script>

<div bind:this={div} contenteditable on:focus="{handleFocus}">This is some sample text, click on me</div>
<textarea bind:this={textarea} ></textarea>
  •  Tags:  
  • Related