Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I add event handlers to the body element in Elm?

Tags:

elm

I'm trying with the Html.App.beginnerProgram and I want to add event handlers (onKeyDown, etc.) to the <body> element.

Unfortunately everything I put in view becomes the children of <body>. Returning Html.body from view doesn't do the trick. This code:

main = beginnerProgram { model = 0, view = view, update = update }

view model = body [] []

update _ model = model

will generate:

<html>
    <head>...</head>
    <body>
        <body></body>
    </body>
</html>

So, how do I get the control of the <body> element?

like image 208
Minoru Avatar asked Sep 23 '16 03:09

Minoru


1 Answers

Since Elm renders as a descendant of <body/>, you cannot bind event handling to it in the normal Elm way (e.g. button [ onClick SomeMsg ] []). Instead you'll have to use ports. And because you're using ports and subscriptions, you will need to use Html.App.program rather than beginnerProgram.

You'll need a port function inside your port module:

port bodyKeyPress : (String -> msg) -> Sub msg

And then you'll be able to send key presses to that port from javascript:

app.ports.bodyKeyPress.send(...);

Here's a full example:

Main.elm

port module Main exposing (main)

import Html.App as App
import Html exposing (..)

main = App.program
  { init = init
  , update = update
  , view = view
  , subscriptions = subscriptions
  }

type alias Model = { message : String } 

type Msg = BodyKeyPress String

init = { message = "Press some keys: " } ! []

update msg model =
  case msg of
    BodyKeyPress c ->
      { model | message = model.message ++ c } ! []

view model = text model.message

subscriptions _ =
  bodyKeyPress BodyKeyPress

port bodyKeyPress : (String -> msg) -> Sub msg

And the html behind the scenes (assuming you built using elm make Main.elm --output=Main.js):

<html>
  <body>
    <script src="Main.js"></script>
    <script>
      var app = Elm.Main.fullscreen();

      document.body.onkeypress = function(e) {
        app.ports.bodyKeyPress.send(String.fromCharCode(e.keyCode));
      };
    </script>
  </body>
</html>

You will need to create and handle a port function for every body event you want to send.

like image 114
Chad Gilbert Avatar answered Oct 16 '22 02:10

Chad Gilbert