1. arowM

    Posted

    arowM
Changes in title
+[Elm] DOM更新後にJSコードを実行する
Changes in tags
Changes in body
Source | HTML | Preview
@@ -0,0 +1,55 @@
+
+
+この内容は Elm 0.18 を対象にしています。
+また、[Elm Discourseの回答](https://discourse.elm-lang.org/t/when-is-cmd-actually-processed/1008/2?u=arowm) が元ネタです。
+
+## 結論
+
+`update` 関数で DOM を更新した後に JavaScript を実行したい場合、
+
+```elm
+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`](https://developer.mozilla.org/ja/docs/Web/API/Window/requestAnimationFrame) に登録します。
+その直後に、`Cmd Msg` として指定された処理を行います。
+
+`animation-frame` はブラウザ画面のリフレッシュレートに応じた間隔で処理を呼び出すため、
+特に工夫なく ports を使って JavaScript の処理を Cmd として登録した場合、画面の更新処理よりも先にその JavaScript が実行されてしまう可能性があります。
+
+## 解決方法
+
+画面の更新処理を `animation-frame` に登録しているので、実行したい JavaScript も同じように `animation-frame` に登録すれば、 `animation-frame` がタスクキューとして実行順を保証してくれます。
+
+具体的には、冒頭で「結論」として示したように
+
+```elm
+elm.app.ports.foo.subscribe(function (id) {
+ requestAnimationFrame(function () {
+ /* when this callback executes, the view should have rendered. */
+ })
+})
+```
+
+のような形式で、実行したい処理を `animation-frame` に登録する処理を記述すれば良いことになります。
+
+
+[`elm-lang/dom`](http://package.elm-lang.org/packages/elm-lang/dom/latest) などは内部的に `animation-frame` に処理を登録する実装になっているため、更新後に初めて作成される HTML タグに対して `elm-lang/dom` で定義されているスクロールやフォーカスなどの処理を適用する際には、特に気にすることなく `update` の返り値に更新後のModelとともに処理を登録すればうまく動きます。
+
+
+[![さくらちゃん](https://qiita-image-store.s3.amazonaws.com/0/16345/86b98051-3fd0-d773-523f-8546b63606e6.jpeg)](https://twitter.com/hashtag/%E3%81%95%E3%81%8F%E3%82%89%E3%81%A1%E3%82%83%E3%82%93%E6%97%A5%E8%A8%98?src=hash)