---
title: Building custom DOM event handlers in Elm
teaser: A step-by-step guide to implementing a custom `onChange` event in Elm 0.18.
tags: web,elm
author: Joël Quenneville
published_on: 2016-11-21
---

You're just getting started with Elm 0.18. You've gotten used to the basics of
the Elm architecture, and how to use the [`Html.Events` package] to listen to DOM
events. Time to move on to building a more complex form.

You start by adding a select:

```elm
initialModel : List String
initialModel =
  [ "Dog"
  , "Cat"
  , "Hamster"
  ]

view : List String -> Html Msg
view animals =
  select [] (List.map animalOptions animals)

animalOptions : String -> Html a
animalOptions animal =
  option [] [ text animal ]
```

Looking good! Now all you need is to wire up the `change` event. Hmmm... it
looks like `Html.Events` doesn't provide an `onChange` event handler. What do
you do now?

[`Html.Events` package]: http://package.elm-lang.org/packages/elm-lang/html/latest/Html-Events

## Event handlers in Elm

While `Html.Events` provides event handlers for the most common events, it does
not provide then for all possible events (see [this issue] for a discussion on
adding `onChange`). However, it does expose a few
lower-level functions that you can use to implement your own custom event
handler. If you look at the source of the pre-packaged event handlers, they are
implemented in terms of these lower-level functions.

A custom event handler is composed of four parts:

1. The [`Html.Events.on`] function to create the handler
2. The name of a DOM event
3. A `Msg` constructor
4. A JSON decoder

[`Html.Events.on`]: http://package.elm-lang.org/packages/elm-lang/html/latest/Html-Events#on
[this issue]: https://github.com/elm-lang/html/issues/23

## Plain old DOM events

When a user selects an option from a dropdown, it broadcasts a `change` event
along with some metadata. That metadata looks like:

```js
{
  bubbles : true,
  cancelBubble : false,
  cancelable : false,
  currentTarget : null,
  defaultPrevented : false,
  target : {...},
  type : "change"
  // ... more
}
```

In most applications, what we're interested in is the `target` key that points
to the DOM node where the event originated (i.e. our `select`). Once we have
that DOM node, we can access its current value.

## Decoders

In order to pull a value out of a JavaScript object and into Elm, we'll need to
use a JSON decoder. You can make your own but `Http.Events` ships with a few
convenient decoders to interact with the DOM event.

The most commonly used is [`Html.Events.targetValue`] which extracts the string
from `event.target.value`.

[`Html.Events.targetValue`]: http://package.elm-lang.org/packages/elm-lang/html/latest/Html-Events#targetValue

## Tagging

Great, we can get strings when a given DOM event fires. However, event handlers
need to give us back a `Msg` type to be handled by our `update` function, not a
string. We are going to want to wrap this string with our `Msg` (a practice
often called "tagging").

To transform the result of a JSON decoder, we can use the [`Json.Decode.map`]
function:

```elm
type Msg = AnimalSelected String

selectedAnimalDecoder : Json.Decoder Msg
selectedAnimalDecoder =
  Json.Decode.map AnimalSelected Html.Events.targetValue
```

[`Json.Decode.map`]: http://package.elm-lang.org/packages/elm-lang/core/latest/Json-Decode#map

## Putting it all together

Finally, you combine all these pieces together and allow a generic tagger to be
passed in:

```elm
type Msg = AnimalSelected String

onChange : (String -> msg) -> Html.Attribute msg
onChange tagger =
  on "change" (Json.Decode.map tagger Html.Events.targetValue)

view : List String -> Html Msg
view animals =
  select [ onChange AnimalSelected ] (List.map animalOptions animals)
```

Because of the nice lower-level function provided, writing a custom `onChange`
event handler is a one-liner!

## Further reading

Read [the source] for `Html.Events` to get more examples of event handlers.
They're all one-liners too; no magic going on here.

[the source]: https://github.com/elm-lang/html/blob/2.0.0/src/Html/Events.elm
