結論
- Navigation.load id
検索ワードが思い浮かばなくて、なかなか上記の結論にたどり着けませんでした。
a [ onClick (PageAnchor "#some_id") ] [ text "ジャンプ!" ]
-- 省略
div [ id "some_id"][]
type Msg
= PageAnchor String
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
PageAnchor id ->
( model, Navigation.load id )
きっかけ
縦に長いページを作っていて、目次を作って飛べるようにしたかった。
ただ、Browser.applicationを使っていると、aタグのhrefに#idのように指定しても、
URLが変わるだけでページ内遷移しない。
失敗したパターン
https://package.elm-lang.org/packages/elm/browser/latest/Browser-Dom#setViewportOf
「Change the x and y offset of a DOM node’s viewport by ID. 」って書いてあって、
DOMの位置までスクロールしてくれるのかな、と思ったけれどそんなことはなかった。
そもそも、上のリンク先にあるサンプルコードをいれると以下のエラーがでた。
Dom.getViewportOf id
|> Task.andThen (\info -> Dom.setViewportOf id 0 info.scene.height)
|> Task.perform (\_ -> NoOp)
^^^^^^^^^^^^^^^^^^^^^^^^
| The argument is:
| Task Dom.Error ()
| But (|>) is piping it a function that expects:
| Task Never ()
以下のように、Task.attemptに変えたら動いたけれど、なんで動かないコードが載ってるんだろう。
performが指定してあるということは、以前は失敗しなかったのかしら?
import Browser.Dom as Dom
import Task
type Msg = NoOp
jumpToBottom : String -> Cmd Msg
jumpToBottom id =
Dom.getViewportOf id
|> Task.andThen (\info -> Dom.setViewportOf id 0 info.scene.height)
|> Task.attempt (\_ -> NoOp)
中途半端な成功
さて、失敗したときに、何も起こらなかったら完全にelmが動いていないのだなと思うのだけれど、
jumpToBottom id を使ったときには、新しく開いたタブに直接http://hoge/fuga#some_id と打てば、
その場所まで遷移するという動きをしていた。
助言
discordのbeginnersスレッドで困っていたら、ヤギの方が助言をくださった。
「setViewPortOf は、IDで指定した スクロール可能な 要素を任意の位置にスクロールするものであって、スクロール可能な要素に 含まれる要素 をIDで指定してその要素のところまでスクロールさせるものではないです。」
「新しいタブにURLを入れてアクセスした時は、ブラウザがハッシュ値を見てブラウザの機能でそのハッシュ値のIDを持つ要素のところまでスクロールしてるからですね!」
え、ブラウザの機能より前にelm動いてるの。意外。
もしかしたら暗黙的にNavigation.load idが呼ばれてるかもだけど。
解決
「Navigation.load "#id" を実行したら、実はページリロード無しで id 要素までスクロールしてくれたりしないでしょうか?」
Navigationってこれかー。
https://package.elm-lang.org/packages/elm/browser/latest/Browser-Navigation
すんなりうまくいきました。
補足
内部的には普通にwindow.location呼んでるんですね。
jsでどうやるかは知ってても、elmでどうやるかに応用がうまくできないなぁと。
とりあえず、このあたりの機能は Browser モジュールにないか探してみようと思いました。
https://github.com/elm/browser/blob/1.0.1/src/Elm/Kernel/Browser.js#L451
https://developer.mozilla.org/ja/docs/Web/API/Window/location