# Modifying the string in a text box pushes the cursor to the end

**URL:** <https://discourse.elm-lang.org/t/modifying-the-string-in-a-text-box-pushes-the-cursor-to-the-end/6151>\
**Category:** Learn\
**Created:** [August 11, 2020, 6:14pm UTC](https://discourse.elm-lang.org/t/modifying-the-string-in-a-text-box-pushes-the-cursor-to-the-end/6151 "2020-08-11T18:14:53Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![AlanQ](https://avatars.discourse-cdn.com/v4/letter/a/e9bcb4/32.png) [@AlanQ](https://discourse.elm-lang.org/u/AlanQ)\
**Post date:** [August 11, 2020, 6:14pm UTC](https://discourse.elm-lang.org/t/modifying-the-string-in-a-text-box-pushes-the-cursor-to-the-end/6151/1 "2020-08-11T18:14:54Z")

</div>

I need to correct text that is input in a text box, character-by-character.  
Elm makes this easy with `onChange` in elm-ui, and `onInput` in Html.

However, when the corrected string is put into the box (via the model), the cursor (caret) jumps to the end of the string — very annoying for the user if they are editing the string somewhere in the middle.

If the modification process doesn’t actually modify the string, then the cursor stays where it should be. For example:

```
  SimpleTextBoxChanged string ->
     let
        newString = String.filter (\c -> c /= '#') string 
     in
        ( { model | simpleTextBoxString = newString }, Cmd.none )

```

Enter (or delete) anything but a ‘#’ and the cursor is where it should be.  
Enter a ‘#’ and it is correctly filtered out _but_ the cursor jumps to the end.

My current solution is to use JavaScript via a port to re-adjust the cursor using [`setSelectionRange`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/setSelectionRange).

Which is not ideal. Especially as it’s important to change the text in the box (by updating the model) _before_ moving the cursor (by issuing the port command). And there seems to be no way to run a command _after_ the model update.

I have solved this potential problem by setting the text string in JavaScript (as well as in the Elm model) using the `.value` function on the textbox element, and _then_ doing the `setSelectionRange`.

This is a mess. Is there a better way?

---

<div class="post-metadata">

**Author:** ![jessta](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/jessta/32/660_2.png) [@jessta](https://discourse.elm-lang.org/u/jessta)\
**Post date:** [August 12, 2020, 2:56am UTC](https://discourse.elm-lang.org/t/modifying-the-string-in-a-text-box-pushes-the-cursor-to-the-end/6151/2 "2020-08-12T02:56:25Z")

</div>

A `Cmd msg` returned from `update` is dispatched immediately before the call to `view`. In the case of a `Cmd msg` calling a port, the port will be called synchronously so the `view` won’t be updated at the time your JS is run. The general solution to this is to delay the JS from running until the next animation frame which will be after the `view` has been updated. You can do this in JS with setTimeout() or with requestAnimationFrame() called within the function you’ve subscribed to the port on the JS side.

---

<div class="post-metadata">

**Author:** ![Atlewee](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/atlewee/32/4603_2.png) [@Atlewee](https://discourse.elm-lang.org/u/Atlewee)\
**Post date:** [August 12, 2020, 5:33am UTC](https://discourse.elm-lang.org/t/modifying-the-string-in-a-text-box-pushes-the-cursor-to-the-end/6151/3 "2020-08-12T05:33:34Z")

</div>

To make it cleaner and more robust/reusable you could probably create a custom-element that takes in “cursor/caret” position as an attribute. Or create that custom element in a way that does not move the cursor to the end on updates.

---

<div class="post-metadata">

**Author:** ![joakin](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/joakin/32/171_2.png) [@joakin](https://discourse.elm-lang.org/u/joakin)\
**Post date:** [August 12, 2020, 9:13am UTC](https://discourse.elm-lang.org/t/modifying-the-string-in-a-text-box-pushes-the-cursor-to-the-end/6151/4 "2020-08-12T09:13:12Z")

</div>

> [@jessta](#):
>
> requestAnimationFrame() called within the function you’ve subscribed to the port on the JS side.

Exactly this. I do the same thing, just wrap your cursor setting code inside `requestAnimationFrame`.

You can also send selection information to the Elm app as needed if it helps in a port, or dispatching events on the html elements and listen to them from Elm (or even making a custom element too).

Some snippets I have:

- Setting a selection in some random contenteditable DOM:

```elm
        app.ports.setCursor.subscribe(range => {
          requestAnimationFrame(() => {
            let cursorRange = document.createRange();

            let selection = window.getSelection();

            let startContainer = document.getElementById(range.start.id);
            if (!startContainer)
              throw new Error(
                `Couldn't find element with id ${range.start.id}`
              );
            cursorRange.setStart(startContainer.firstChild, range.start.offset);
            if (range.end) {
              let endContainer = document.getElementById(range.end.id);
              if (!endContainer)
                throw new Error(
                  `Couldn't find element with id ${range.end.id}`
                );
              cursorRange.setEnd(endContainer.firstChild, range.end.offset);
            }

            selection.removeAllRanges();
            selection.addRange(cursorRange);
          });
        });
      });

```

- Listening to selection changes (which you can then send to the Elm app as needed:

```elm
      document.addEventListener("selectionchange", () => {
        console.log("SELECTION CHANGED", document.getSelection());
      });

```

---

<div class="post-metadata">

**Author:** ![lydell](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/lydell/32/178_2.png) [@lydell](https://discourse.elm-lang.org/u/lydell)\
**Post date:** [August 12, 2020, 12:15pm UTC](https://discourse.elm-lang.org/t/modifying-the-string-in-a-text-box-pushes-the-cursor-to-the-end/6151/5 "2020-08-12T12:15:18Z")

</div>

I made an example on how to make a field where it is only possible to type digits a while ago. Maybe it can be of inspiration. [https://ellie-app.com/6YfzVxqmtJ3a1](https://ellie-app.com/6YfzVxqmtJ3a1)

---

<div class="post-metadata">

**Author:** ![jreusch](https://avatars.discourse-cdn.com/v4/letter/j/b5e925/32.png) [@jreusch](https://discourse.elm-lang.org/u/jreusch)\
**Post date:** [August 12, 2020, 4:28pm UTC](https://discourse.elm-lang.org/t/modifying-the-string-in-a-text-box-pushes-the-cursor-to-the-end/6151/6 "2020-08-12T16:28:46Z")

</div>

Fixing the cursor position afterwards by setting it to its old starting offset seems to do the trick in most cases?

```auto
window.addEventListener('input', event => {
    // capture the value the browser set
    const target = event.target
    const { value, selectionStart } = target
    
    // wait for Elm to decide what the new value
    // of the input actually should look like
    requestAnimationFrame(() => {
        const newValue = target.value
        if (value !== newValue) {
            target.selectionEnd = target.selectionStart =
                selectionStart - (value.length - newValue.length)
        }
    })      
}, true)

```

I haven’t really tested this that much though, I suspect there are some cases where it doesn’t do “the right thing”. Still it seems so simple and more correct than whatever the browser is currently doing, I wonder why this is not just the default…

* * *

Every time I deal with “custom” controls and forms in browsers I think about just replacing HTML alltogether

---

<div class="post-metadata">

**Author:** ![berend](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/berend/32/2250_2.png) [@berend](https://discourse.elm-lang.org/u/berend)\
**Post date:** [August 12, 2020, 7:15pm UTC](https://discourse.elm-lang.org/t/modifying-the-string-in-a-text-box-pushes-the-cursor-to-the-end/6151/7 "2020-08-12T19:15:41Z")

</div>

> [@jessta](#):
>
> A `Cmd msg` returned from `update` is dispatched immediately before the call to `view` .

That’s very interesting to know. Is this 100% guaranteed? Elm doesn’t check that 16.7ms have passed, so it needs to update the screen in order to maintain framerate for example?

---

<div class="post-metadata">

**Author:** ![AlanQ](https://avatars.discourse-cdn.com/v4/letter/a/e9bcb4/32.png) [@AlanQ](https://discourse.elm-lang.org/u/AlanQ)\
**Post date:** [August 12, 2020, 7:27pm UTC](https://discourse.elm-lang.org/t/modifying-the-string-in-a-text-box-pushes-the-cursor-to-the-end/6151/8 "2020-08-12T19:27:21Z")

</div>

Great replies. Thank you to all.

* * *

@jessta

That’s good to know. You’ve filled an important gap in my understanding of the Elm runtime timeline.

* * *

@Atlewee

Is this a learning curve I see before me? 🙂

Thank you, an interesting idea. Especially the tantalising prospect to “create that custom element in a way that does not move the cursor to the end on updates.”.

One of the benefits of Elm, if I understand correctly, is that it compiles to old JavaScript and therefore runs on quite old browsers. Looking at [caniuse.com](https://caniuse.com/#feat=custom-elementsv1) makes me a little wary of custom elements.

* * *

@joakin

Brilliant. Many thanks.

* * *

@lydell

That’s neat. Do it all in JS, with a well supported event listener (input) and a class to decide which boxes to apply it to 🙂

* * *

@jreusch

Thank you, that’s what I’m doing, just without the `requestAnimationFrame` — which I will now add having learnt from the replies here that that’s how to get the timing right.

It’s good to know others have largely thought it through the same way as me.

---

<div class="post-metadata">

**Author:** ![system](https://canada1.discourse-cdn.com/flex035/uploads/elm_lang/original/1X/50a05e53677a2c3b47776d7abd0f113eb50193a1.png) [@system](https://discourse.elm-lang.org/u/system)\
**Post date:** [August 22, 2020, 7:27pm UTC](https://discourse.elm-lang.org/t/modifying-the-string-in-a-text-box-pushes-the-cursor-to-the-end/6151/9 "2020-08-22T19:27:24Z")

</div>

Closed on August 22, 2020, 7:27pm UTC

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.
