1. arowM

    No comment

    arowM
Changes in body
Source | HTML | Preview
@@ -1,82 +1,82 @@
-この内容は Elm 0.18 を対象にしています。
+この内容は Elm 0.18, 0.19 を対象にしています。
また、[Elm Discourseの回答](https://discourse.elm-lang.org/t/when-is-cmd-actually-processed/1008/2?u=arowm) が元ネタです。
## 結論
`update` 関数で DOM を更新した後に JavaScript を実行したい場合、
```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`](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` がタスクキューとして実行順を保証してくれます。
具体的には、冒頭で「結論」として示したように
```javascript
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.com/arowM/items/93201dd5aded6e264e19)を使っても「DOM 構造に依存するJavaScriptを実行する」という需要を叶えることができます。
単純に、View に `script` タグを直接埋め込むだけです。
```haskell
someView : Model -> Html Msg
someView { showPart } =
div
[]
[ if showPart then
div
[]
[ anotherView
, node "script" [] [ text """ /* JavaScript code */ """ ]
]
else
text ""
]
```
ただ、この手法は[「バグ」としてあつかわれており](https://github.com/elm-lang/html/issues/56)、次のバージョンの Elm では使えなくなる可能性が高いです。
非推奨な危険🐐スクリプト芸ではなく、`animation-frame` を使った方法を今のバージョンでも使うことをオススメします。
[![さくらちゃん](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)