読者です 読者をやめる 読者になる 読者になる

PureScript で DOM を操作

Haskell JavaScript PureScript

PureScript の下記ライブラリを使って簡単な DOM 操作を試してみました。

ソースは http://github.com/fits/try_samples/tree/master/blog/20160125/

はじめに

PureScript を使って実装するものと同等の処理を JavaScript で書いてみました。 id で指定した DOM ノードの textContent を変更するだけの簡単な処理です。

sample.js
var Sample = {
    updateContent: (id, content) => {
        var node = document.getElementById(id);

        if (node) {
            node.textContent = content;
        }
    }
};

下記の HTML で実行してみます。 (PureScript の方は updateContent の呼び出し部分が少し異なります)

index.html
<!DOCTYPE html>
<html>
<body>
    <h2 id="d"></h2>

    <script src="sample.js"></script>
    <script>
        Sample.updateContent('d', 'sample javascript');
    </script>
</body>
</html>
実行結果

Web ブラウザで表示した結果は以下の通りです。

f:id:fits:20160125204319p:plain

purescript-dom の場合

pulp init でプロジェクトを作成し、pulp dep installpurescript-dom をインストールします。

なお、pulpgulp を事前にインストール (npm install) しておきます。

purescript-dom インストール
> pulp init
・・・

> pulp dep install purescript-dom --save

src/Main.purs を編集し updateContent 関数を実装します。

以下のように型まわりに注意が必要です。

  • (a) document 関数は Eff (dom :: DOM | eff) HTMLDocument を返す
  • (b) getElementById 関数の引数は ElementId と NonElementParentNode
  • (c) getElementById 関数は Eff (dom :: DOM | eff) (Nullable Element) を返す
  • (d) setTextContent 関数の引数は String と Node

(a) の結果の HTMLDocument を getElementById の引数へそのまま使えなかったので htmlDocumentToNonElementParentNode 関数で変換しています。

(c) の結果の Nullable はそのままだと使い難いので toMaybe 関数で Maybe 化し、Element も setTextContent の引数に使えなかったので elementToNode 関数で変換しています。

src/Main.purs
module Main where

import Prelude
import Control.Monad.Eff

import Data.Maybe
import Data.Nullable (toMaybe)

import DOM
import DOM.HTML.Types
import DOM.Node.Types
import DOM.HTML (window)
import DOM.HTML.Window (document)
import DOM.Node.NonElementParentNode (getElementById)
import DOM.Node.Node (setTextContent)

updateContent :: forall eff. String -> String -> Eff (dom :: DOM | eff) Unit
updateContent id content = do
    win <- window
    doc <- document win
    node <- getElementById (ElementId id) $ htmlDocumentToNonElementParentNode doc
    case (toMaybe node) of
        Just x -> setTextContent content (elementToNode x)
        _      -> return unit

今回は、gulp を使って pulp browserify を実行するように以下のような gulpfile.js を用意しました。

Sample.updateContent で関数を実行できるように --standalone を指定しています。

gulpfile.js
var gulp = require('gulp');
var child_process = require('child_process');

var pulpCmd = (process.platform == 'win32')? 'pulp.cmd': 'pulp';
var destFile = 'sample.js'

gulp.task('pulp_package', () => {
    // pulp browserify の実行
    var res = child_process.spawnSync(pulpCmd, ['browserify', '--standalone', 'Sample', '-t', destFile]);

    // 実行結果の出力
    [res.stdin, res.stdout, res.stderr].forEach( x => {
        if (x) {
            console.log(x.toString());
        }
    });
});

gulp.task('default', ['pulp_package']);

gulp コマンドを実行すると sample.js が生成されます。

ビルド例 (gulp で pulp browserify を実行)
> gulp

下記の HTML で実行してみます。

ここで、Sample.updateContent はカリー化されており function(id) { return function(content) { return function __do() { ・・・ } } } となっている点に注意。

index.html
<!DOCTYPE html>
<html>
<body>
    <h2 id="d"></h2>

    <script src="sample.js"></script>
    <script>
        Sample.updateContent('d')('sample purescript-dom')();
    </script>
</body>
</html>
実行結果

f:id:fits:20160125204344p:plain

purescript-simple-dom の場合

同じ様にして purescript-simple-dom をインストールします。

purescript-simple-dom インストール
> pulp init
・・・

> pulp dep install purescript-simple-dom --save

src/Main.purs を編集し updateContent 関数を実装します。

purescript-dom と比べると余計な型変換が不要なのでシンプルです。

src/Main.purs
module Main where

import Prelude
import Control.Monad.Eff

import DOM

import Data.Maybe
import Data.DOM.Simple.Window (document, globalWindow)
import Data.DOM.Simple.Element (getElementById, setTextContent)

updateContent :: forall eff. String -> String -> Eff (dom :: DOM | eff) Unit
updateContent id content = do
    doc <- document globalWindow
    node <- getElementById id doc

    case node of
        Just x -> setTextContent content x
        _      -> return unit

purescript-dom と同じ様に gulp で sample.js を生成しました。(gulpfile.js は同じ内容です)

HTML は以下の通りです。

index.html
<!DOCTYPE html>
<html>
<body>
    <h2 id="d"></h2>

    <script src="sample.js"></script>
    <script>
        Sample.updateContent('d')('sample purescript-simple-dom')();
    </script>
</body>
</html>
実行結果

f:id:fits:20160125204359p:plain