25
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

[Elm] DOM更新後にJSコードを実行する

Last updated at Posted at 2018-04-14

この内容は Elm 0.18, 0.19 を対象にしています。
また、Elm Discourseの回答 が元ネタです。

結論

update 関数で DOM を更新した後に JavaScript を実行したい場合、

elm.app.ports.foo.subscribe(function (id) {
  requestAnimationFrame(function () {
    /* when this callback executes, the view should have rendered. */
  })
})

のように ports を登録する必要があります。

問題提起

Elm の update 関数は、型 update : Model -> Msg -> (Model, Cmd Msg) で示されるように、なんらかのイベント (Msg で示されるもの) に応じて、更新後のDOM構造 (厳密にはDOM構造そのものではなく Model) と副作用 (Cmd Msg) を同時に指定します。

ports を用いて JavaScript を実行する場合、Cmd Msg の部分にその処理を記述しますが、
更新後のDOMに依存する JavaScript コードを実行したい場合はどうしたらいいでしょうか?

画面更新と Cmd の処理タイミング

update 関数によって新しい DOM 構造が指定されると、Elm はその更新処理を animation-frame に登録します。
その直後に、Cmd Msg として指定された処理を行います。

animation-frame はブラウザ画面のリフレッシュレートに応じた間隔で処理を呼び出すため、
特に工夫なく ports を使って JavaScript の処理を Cmd として登録した場合、画面の更新処理よりも先にその JavaScript が実行されてしまう可能性があります。

解決方法

画面の更新処理を animation-frame に登録しているので、実行したい JavaScript も同じように animation-frame に登録すれば、 animation-frame がタスクキューとして実行順を保証してくれます。

具体的には、冒頭で「結論」として示したように

elm.app.ports.foo.subscribe(function (id) {
  requestAnimationFrame(function () {
    /* when this callback executes, the view should have rendered. */
  })
})

のような形式で、実行したい処理を animation-frame に登録する処理を記述すれば良いことになります。

elm-lang/dom などは内部的に animation-frame に処理を登録する実装になっているため、更新後に初めて作成される HTML タグに対して elm-lang/dom で定義されているスクロールやフォーカスなどの処理を適用する際には、特に気にすることなく update の返り値に更新後のModelとともに処理を登録すればうまく動きます。

危険🐐スクリプト芸との関わり

実は危険🐐スクリプト芸を使っても「DOM 構造に依存するJavaScriptを実行する」という需要を叶えることができます。

単純に、View に script タグを直接埋め込むだけです。


someView : Model -> Html Msg
someView { showPart } =
    div
        []
        [ if showPart then
            div
                []
                [ anotherView
                , node "script" [] [ text """ /* JavaScript code */ """ ]
                ]
          else
            text ""
        ]

ただ、この手法は「バグ」としてあつかわれており、次のバージョンの Elm では使えなくなる可能性が高いです。

非推奨な危険🐐スクリプト芸ではなく、animation-frame を使った方法を今のバージョンでも使うことをオススメします。

さくらちゃん

25
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
25
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?