Right clicks & Elm
Tags: elm
Published: 2017-12-08 14:04:08

---
title: Right clicks & Elm
published: true
description: How to decode buttons for mouse events
tags: elm
---

At [Zaptic](https://www.zaptic.com), we allow our customers to create a flow chart describing the business processes that they would like to complete. We draw the flow chart in SVG using a pan & zoom interface to make it easy to look around. We track mouse down, mouse drag & mouse up events to manage this UI.

Unfortunately, in development, the experience was a little annoying as every time we loaded the browser's inspector on an element the `mousedown` of the right-click would be tracked by the UI but the `mouseup` would be over the context menu and so would not register. The result? The UI gets left in "pan" mode and attempts to follow your mouse everywhere even though you don't have any mouse buttons pressed any more.

To overcome this, we needed to look into tracking which mouse button was being used so that we could ignore the right-clicks. We are using the [elm-lang/mouse](http://package.elm-lang.org/packages/elm-lang/mouse/latest) package which includes a `position` decoder that can be used for `mousedown` events. That decoder only provides the mouse location as `pageX` and `pageY`, it doesn't provide any information about which button was pressed so we need to go a little deeper.

If we take a look at the [source code](https://github.com/elm-lang/mouse/blob/1.0.1/src/Mouse.elm) for the module we can see that the position decoder is doing the following:

```elm
position : Json.Decoder Position
position =
  Json.map2 Position
    (Json.field "pageX" Json.int)
    (Json.field "pageY" Json.int)
```

It is looking for the `pageX` and `pageY` properties, as we suspected, and it expects them to be integers. If we look at the lovely [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/Events/mousedown) for a mousedown event we can see that it has lots of other properties including a integer `button` property which contains some indication of which button is being used. We can set up our own type & decoder as:

```elm
type alias MouseClick =
    { pageX : Int
    , pageY : Int
    , button : Int
    }


mouseClickDecoder : Json.Decode.Decoder PFM.MouseClick
mouseClickDecoder =
    Json.Decode.map3 PFM.MouseClick
        (Json.Decode.field "pageX" Json.Decode.int)
        (Json.Decode.field "pageY" Json.Decode.int)
        (Json.Decode.field "button" Json.Decode.int)

```

And adapt our event handler to use it:

```elm
type Msg
    = DragStart MouseClick
    | ...

onMouseDown : Attribute Msg
onMouseDown =
    on "mousedown" (Json.Decode.map DragStart mouseClickDecoder)
```

Then in our `update` function that is going to handle the `DragStart` message we can check the value of `button` and ignore right-clicks. There are some inconsistencies between browsers on how different buttons are represented as integers which you can read about on [quirksmode](https://www.quirksmode.org/js/events_properties.html#button). Fortunately they all align for the 'right-click' which is represented as `2`. So we can ignore that:

```elm
DragStart click ->
    -- Ignore right-clicks which are given value '2' by browsers
    if click.button /= 2 then
        let
            pos =
                { x = click.pageX, y = click.pageY }
        in
        ({ model | = drag = Just { start = pos, current = pos } }, Cmd.none)
    else
        (model, Cmd.none)
```

And there we have it! We're free of the right-clicks and can happily jump into the browser dev-tools whenever we like.

If you have any thoughts or advice, please let me know! Always happy to learn.

*Update*: Checkout Ilias' comment below. It really cleans up the message handling in the update:

```diff
-        DragStart click ->
-            -- Ignore right-clicks which are given value '2' by browsers
-            if click.button /= 2 then
-                let
-                    pos =
-                        { x = click.pageX, y = click.pageY }
-                in
-                { model | drag = Just { start = pos, current = pos } } ! []
-            else
-                model ! []
+        DragStart pos ->
+            { model | drag = Just { start = pos, current = pos } } ! []
```