Elm Journey - Avoiding HTML inputs
Tags: elm
Published: 2018-06-28 18:36:14

---
title: Elm Journey - Avoiding HTML inputs
published: true
description: Dueling states are no fun.
tags: #elm
---

I have started trying to think of ways to avoid using HTML inputs in my apps when using Elm. I wanted to share more about this and where I have gotten so far.

## Why?

HTML inputs have their own independent state, separate from your own Elm `Model`. When these differ, you can observe some confusing and difficult-to-trace behavior.

The first examples of this that we ran into were radio buttons. If you give several radio buttons the same `name`, then the **browser** will treat them as one group and keep track of which one the user last selected. However, Elm is not aware of this unless you setup click events and appropriately update the model. This caused some initial confusion because it looks like everything works even though the model is not getting updated. A couple of us with Angular experience had trouble following what was happening because we were so used to two-way binding.

Another common example of the problem is text inputs where the user types very fast. _This not a problem in most cases. But we ran into it with USB barcode scanners which enter numbers very quickly._

For example, if the user types "abc" _very_ quickly, the "b" will get dropped. Here is why.

1. User types "a", browser changes input value to "a"
2. `UserEntered "a"` sent to Elm
3. User types "b", browser changes input value to "ab"
4. `UserEntered "ab"` sent to Elm
5. Elm processes `UserEntered "a"`, updates model, rerenders
6. Elm changes the input value to "a" (replacing "ab")
7. User types "c", browser changes input value to "ac"
8. `UserEntered "ac"` sent to Elm
9. "ab" will get processed, but will later be overwritten by "ac"
10. To the user it looks like "b" got dropped.

**Issues like these arise because HTML inputs have their own state which can be updated from many different places at once.** In Elm apps, we would prefer to be the keeper of the state.

This is unfortunately not a solved problem for text inputs, as you will see below, but other types of inputs can be replaced with display-only HTML.


### Radio buttons and Checkboxes

Once you realize the problem you could continue to use these inputs... just don't set the `name` attribute (nor is `value` necessary). But for styling flexibility, I tend to prefer to replace these with display-only elements. For example, you can use an icon and text. Here is a checkbox replacement example using Semantic UI.

![active toggle switch](https://thepracticaldev.s3.amazonaws.com/i/dyjax4src8lpdkbxdwrj.png)

```elm
div []
  [ i [ class "input-sized blue toggle on icon" ] []
  , b [ class "gap left" ] [ text " Active" ]
  ]
```

![inactive toggle switch](https://thepracticaldev.s3.amazonaws.com/i/o8dqnudoex0zpxwq512l.png)

```elm
div []
  [ i [ class "input-sized horizontally flipped grey toggle on icon" ] []
  , span [ class "gap left" ] [ text " Inactive" ]
  ]
```

I convert the model property to HTML pretty simply like this:

```elm
if something.isActive then
  activeToggle
else
  inactiveToggle
```

_Semantic UI has an `off toggle` icon, but I did not want to use it because it was so deemphasized that it no longer looked like a control. So I used the `on toggle` flipped and turned grey._

The CSS classes in the above snippets are from Semantic UI. With the exception of a two extra styles I used for minor visual tweaks.

```css
.gap.left { margin-left: 3px; }

/* same size as other inputs, i.e. on same line */
i.input-sized.icon, i.input-sized.icons { font-size: 2.7em; line-height: 1; vertical-align: middle }
```


### Select

There are tons of different styled drop-down list replacements. Most UI styling kits (including Semantic, Bootstrap, and Zurb Foundations) have one. One of the reasons I love Semantic is because I can style all its states entirely with HTML classes (no JS), which meshes perfectly with Elm. However, one thing to bear in mind is performance. If you have the scenario where you need a lot of data in a drop-down or a lot of drop-downs on a page __*__, a javascript-rendered drop down is going to be significantly slower than a browser-rendered `select`.

__*__ these are not great design choices, but sometimes needs must.

I generally hated styled drop-down list replacements in the past. But the reason then was because I was still depending on using the state directly from the HTML element. And these replacements had really hokey ways of keeping state which usually required extra code on my part. Whereas getting the state from native HTML elements "just worked". However since I only care about the display aspects when using Elm, a styled replacement is fine... provided it is robust enough.


### Text input

This one is a much harder problem, and not a solved one. Initially I thought about using a `div` and handling onKeyUp, but then how do you handle cursor positioning (use clicks in between letters to move cursor)? Do you really want to have to handle backspace, delete, arrow keys, etc? "contenteditable" is no help either as it has the same statefulness problem of input.

For now I work around the fast input problem mentioned above using the `defaultValue` attribute. Browsers display `defaultValue` if `value` has not been set. However, defaultValue faces new problems where you have a lot of dynamically generated text inputs. Elm's virtual DOM implementation apparently has a bug which does not properly handle this case. In that case, I just use `value` and most users can't type fast enough to experience the problem.

If I were to try to solve this problem, I was thinking I would use a text input for user entry, but have it hidden (opacity 0%) in front of the display-only div. Then have the div styled to look like an input and receive updates from Elm as the user enters text. So the input is only used as a "source" but what is displayed is the div's contents rendered from Elm. But that's just a vague idea at this stage.

### Other inputs: number, date, etc.

I generally find these are inconsistently implemented across browsers. So rather than writing a bunch of code to resolve that inconsistency, I use text inputs for everything.

## Conclusion

This is still a work in progress for me. The main revelation is that I don't need HTML elements to keep state for me when using Elm. And in fact, using stateful elements has caused the proverbial "wild goose chase" on the team more than once. In an ideal scenario I want to use HTML only for visual purposes. However, text input continues to be a sticking point since it handles so many important dimensions of data entry for me. If anybody has any further ideas there, I would love to hear them.

/∞