Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

この内容は 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 を使った方法を今のバージョンでも使うことをオススメします。

さくらちゃん

arowM
さくらちゃんはアーティストだよ やりたくないことはやりたくないよ Elmとかを草の種にしてるよ フリーランスUXハッカーだよ ARoW(システム開発)代表 http://arow.info /気吹堂(出版)代表/人材紹介会社CXO http://github.com/arowM お仕事依頼はTwitterへ
https://arow.info
arow-oss
ヘテロジニアスで自律分散型な優しい会社です。 従業員がヘテロジニアスな自律分散ノードとして活躍し、代表が汎用ノードとして全体の仕事の調整や、割り振り先がない仕事の処理を担当するボロ雑巾として活躍してます。 フロントエンド側のヘテロジニアスノードがほしいなー
https://arow.info
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした