how to change <h1> tags with ghcjs-dom

174 views Asked by At

I have found the ghcjs and ghcjs-dom documentation very limited. Here is this basic HTML document.

h1 { font-family: Helvetica; }

p {font-family: Helvetica; color: blue; }
<h1>
Hello World
</h1>
<p>
This is my test document.
</p>

I have read that ghcjs merely compiles Haskell to JavaScript. If I want to populate the DOM tree even with this simple document, I need to the Foreign Function Interface (FFI) and possibly ghcjs-dom.

The irony of calling it the "Foreign Function Interface" is that JavaScript is usually considered "native" to the Browser. So there is a tiny bit of terminology confusion there.

In this very simple example, maybe

Let's try a simple example of manipulating the DOM. I have a simple HTML document and I would like to * change the blue paragraph into a red one or * to switch back and forth once each second (between red and blue)

I don't see how the ghcjs set of tools will achieve harder tasks if it cannot even do these very basic test case and explain it. Here is an issue I have raised on Github, with the conclusion that ghcjs lacks a good on-boarding process.

1

There are 1 answers

0
Dave Compton On BEST ANSWER

Here's a short self-contained example that uses reflex-dom to do the red/blue color switching that you described. This is a modified version of the code that epsilonhalbe included in this answer to your earlier question.

{-# LANGUAGE OverloadedStrings #-} 
{-# LANGUAGE ScopedTypeVariables #-} -- allows for local type declarations.
import Reflex
import Reflex.Dom
import Data.Text (Text, pack)
import Data.Map (Map)
import Data.Time.Clock (getCurrentTime)
import Control.Monad.Trans (liftIO)

webPage :: MonadWidget t m => m ()
webPage = do

  -- ticker Event fires once per second.
  ticker :: Event t TickInfo <- tickLossy 1.0 =<< liftIO getCurrentTime  

  -- counter Dynamic increases by one every second.
  counter :: Dynamic t Int <- foldDyn  (\_ n -> n+1) 0 ticker

  -- function to map from integer to red or blue style.
  let chooseColor :: Int -> (Map Text Text) 
      chooseColor n = "style" =: pack ("color: " ++ if (n `mod` 2) == 0 then "red" else "blue")

  -- redBlueStyle Dynamic changes each second.
  let redBlueStyle :: Dynamic t (Map Text Text) 
      redBlueStyle = fmap chooseColor counter

  -- insert an h1 elemnt.
  el "h1" $ text "Hello World!"

  -- insert a paragraph with Dynamic red blue style.
  elDynAttr "p" redBlueStyle $ text "This is my test document"

  return ()


css = "h1 {font-family: Helvetica;} p {font-family: Helvetica;}" 

main :: IO ()
main = mainWidgetWithCss css webPage

Of course, reflex-dom (along with reflex) is a higher level library than ghcjs-dom and it comes with its own set of concepts (Event, Dynamic, Behavior, etc) that you need to get comfortable with.

The example works by creating a Dynamic Map that specifies a style that alternates from red to blue each second and using that Dynamic Map to style an element.

For clarity's sake, this example contains some type declarations that are not strictly required.

This project: https://github.com/dc25/stackOverflow__how-to-change-h1-tags-with-ghcjs-dom contains the above code. Here is a link to a browser based demo: https://dc25.github.io/stackOverflow__how-to-change-h1-tags-with-ghcjs-dom/