-1

I've just started learning Purescript. I'd like to experiment with it in a simple web page. I want to start by learning only plain Purescript and not use any frameworks, such as Halogen or Pux etc.

I believe I'll need to use the purescript-web-html package to register a button click event. I searched for, but didn't find any beginner level examples of using this package to listen for and handle html element events.

<script>
   function log_click() {
      console.log("clicked");
   }

   document.addEventListener('DOMContentLoaded', function() {  
                
      document.getElementById('myButton').onclick = log_click;
   });
</script>
<body>
  <button id="myButton" >click me</button>
</body>

Here's my plain Purescript attempt:

logClick :: Effect Unit
logClick = log "button clicked"

main :: Effect Unit
main = do

  w <- window
  doc <- document w
  buttonMaybe <- getElementById "myButton" $ toNonElementParentNode doc

  myEventTarget <- case buttonMaybe of
    Nothing -> throw "element with id 'myButton' not found."
    Just myButtonElem -> toEventTarget myButtonElem

  let listener =  eventListener logClick 

  addEventListener click listener true myEventTarget
devdanke
  • 1,309
  • 15
  • 27

1 Answers1

2

Your attempt is almost there. The are a few small trivial problems.

First, toEventTarget is a pure function, not an effect. So you can't have it on the right side of <-.

But no fear: if you have a pure value, it's easy to wrap it in an effect by using pure:

  myEventTarget <- case buttonMaybe of
    Nothing -> throw "element with id 'myButton' not found."
    Just myButtonElem -> pure $ toEventTarget myButtonElem

Second, you have the opposite problem with eventListener: it's an effectful function, but you're binding it with let like a pure value. Just use the <- bind:

listener <- eventListener logClick

Finally, the type of parameter eventListener expects is a function Event -> Effect Unit, but your logClick is just an Effect Unit. Just give it an Event parameter. You don't have to actually use it:

logClick :: Event -> Effect Unit
logClick _ = log "button clicked"

And with that, here's the whole working piece:

logClick :: Event -> Effect Unit
logClick _ = log "button clicked"

main :: Effect Unit
main = do
  w <- window
  doc <- document w
  buttonMaybe <- getElementById "myButton" $ toNonElementParentNode doc

  myEventTarget <- case buttonMaybe of
    Nothing -> throw "element with id 'myButton' not found."
    Just myButtonElem -> pure $ toEventTarget myButtonElem

  listener <- eventListener logClick

  addEventListener click listener true myEventTarget

In conclusion, while the above technically works, it can be written a bit shorter and more concisely by composing functions where convenient instead of giving their results names:

main :: Effect Unit
main = do
  window >>= document <#> toNonElementParentNode
  >>= getElementById "myButton"
  >>= traverse_ \button -> do
    listener <- eventListener \_ -> log "button clicked"
    addEventListener click listener true (toEventTarget button)
Fyodor Soikin
  • 78,590
  • 9
  • 125
  • 172
  • Revised my code with your fixes. It works! I'd seen `pure` used in Purescript By Example, but didn't understand what it actually did. You helped me see that it de-Effects things to give a desired type. – devdanke Nov 20 '21 at 10:24
  • Thanks for showing the concise composable style. It'll be a while before I can comprehend it. In the mean time, my clunky verbose style lets me remember how each step works, even when I haven't looked at my code in a few weeks. – devdanke Nov 20 '21 at 10:26
  • BTW, I see you've answered many Purescript questions. It is very kind of you to help people who are using Purescript to increase their understanding of functional programming. I appreciate your friendly, encouraging approach. – devdanke Nov 20 '21 at 10:30
  • Glad to help :-) – Fyodor Soikin Nov 20 '21 at 13:45