---
title: Conditionally handling events in Elm
teaser: Learn how to filter events in Elm and create an `onEnter` event handler!
tags: elm,web
author: Greg Fisher
published_on: 2019-09-20
---

Imagine you need to do something when the enter key is pressed but don't care
when any other keys are pressed. What if you don't _always_ want to react to the
`keyup` event? How would you do it in an Elm app?

I can think of two ways to tackle the problem:

1. Write an `onKeyup` handler and send every event's keycode to the `update`
   function for evaluation.
2. Write an `onEnter` handler and check the keycode in the handler function
   first. Only send on to the `update` function if the enter key was pressed.

The `onKeyup` handler seems like it would be easier to write but having to
check the keycode in our `update` function each time feels clunky.

The `onEnter` handler sounds like a tidier solution. But how do you build that
conditional logic into the handler function?

## How DOM event handler functions work in Elm

In Elm, the [`Html.Events` package] provides the things you need for handling
events, including some built in handlers like [`Html.Events.onClick`]. It also
provides [`Html.Events.on`], the function used to build `Html.Events.onClick`,
and which any developer can use to [build their own custom DOM event handlers].

The way `Html.Events.on` works is that you give it the string name of the event
to react to and a [JSON Decoder] to decode the event object with. Whatever
values you successfully decode are then sent on to your `update` function.

A great example of how this works is `Html.Events.onClick`, which doesn't
extract any values from the event object at all. Instead it uses
[`Json.Decode.succeed`] to ignore the JSON all together and successfully return
`msg`:

```elm
onClick : msg -> Attribute msg
onClick msg =
  on "click" (Json.succeed msg)
```

(Note: `Json.Decode` has been aliased to `Json` in the file where `onClick`
lives.)

The source docs don't mention what happens when the decoder fails. But you can
try it out in [this Ellie App example] and see for yourself. Spoiler alert:
nothing happens. It is allowed to fail silently, and nothing is sent on to the
`update` function. This can [make them tricky to debug], but it gives us a way to
conditionally react to events.

## Sometimes you have to `Json.Decode.fail` to succeed

Elm developers can leverage this behavior to conditionally handle events and
keep all the conditional logic inside of their handler function.

To write an `onEnter` handler this way you would need to do the following:

1. Extract the keycode from the event object with a decoder

2. If the keycode is `13` (the enter key), use `Json.succeed` to return a
   successful message, just like in `onClick`. Otherwise, return `Json.fail`

For the first step, the `Html.Events` package includes a decoder for extracting
the keycode from an event object, called [`Html.Events.keyCode`]. We can use
that.

For the second step, there is a handy function in the `Json` package called
`Json.Decode.andThen` that's especially good at doing just this thing: it lets
us choose a decoder to return, based on the value passed in. Unfortunately,
diving into how `andThen` works is beyond the scope of this short article but is
well worth exploring. In [the mechanics of `Maybe`] my colleague Joël explores
`Maybe.andThen` which works in a similar way to `Json.Decode.andThen`. To
explore decoders in general, Joël has [a few] other [great articles].

Ready to put all this together and make something?

## Implementing `onEnter`

Using everything we just learned we could write an implementation of `onEnter`
like this:

```elm
onEnter : msg -> Html.Attribute msg
onEnter msg =
    let
        isEnterKey keyCode =
            if keyCode == 13 then
                Json.succeed msg

            else
                Json.fail "silent failure :)"
    in
    on "keyup" <|
        Json.andThen isEnterKey Html.Events.keyCode
```

You can [play around with `onEnter` in this Ellie App].

## Summary

The `Html.Events` package authors decided to ignore when decoders fail in
event handlers. We were able to lean on that to write a decoder that decides
whether to fail or not based on some characteristic of the event. In our case,
we were interested in the event's keycode. This allowed us to keep the
conditional logic out of our `update` function and within our custom handler
function.

[`Html.Events` package]:https://package.elm-lang.org/packages/elm/html/latest/Html-Events
[`Html.Events.onClick`]:https://github.com/elm/html/blob/bd01138f617459cc0299e9172e91dd7b48b5f7e9/src/Html/Events.elm#L48
[`Html.Events.on`]:https://github.com/elm/html/blob/bd01138f617459cc0299e9172e91dd7b48b5f7e9/src/Html/Events.elm#L168
[`Html.Events.keyCode`]:https://github.com/elm/html/blob/bd01138f617459cc0299e9172e91dd7b48b5f7e9/src/Html/Events.elm#L310
[build their own custom DOM event handlers]:https://thoughtbot.com/blog/building-custom-dom-event-handlers-in-elm
[this Ellie App example]:https://ellie-app.com/6DYWvXpvRyga1
[a few]:https://thoughtbot.com/blog/5-common-json-decoders
[great articles]:https://thoughtbot.com/blog/getting-unstuck-with-elm-json-decoders
[the mechanics of `Maybe`]:https://thoughtbot.com/blog/maybe-mechanics
[JSON Decoder]:https://package.elm-lang.org/packages/elm/json/latest/Json-Decode
[`Json.Decode.Succeed`]:https://github.com/elm/json/blob/063aaf05e0dc5a642bacbdaae59c33dcfd116898/src/Json/Decode.elm#L595
[play around with `onEnter` in this Ellie App]:https://ellie-app.com/6FX9Y3vPWHNa1
[make them tricky to debug]:https://thoughtbot.com/blog/debugging-dom-event-handlers-in-elm
