从 Elm 端口检索 DOM 值

Retrieving a DOM value from Elm ports

我的 elm 应用程序使用自动滚动功能,它获取元素的 Y 位置并使用 Dom.Scroll.toY 滚动到那里。

两个这样,我设置了两个端口;订阅和发件人。

ports.elm

port setYofElementById : Maybe String -> Cmd msg
port getYofElementById : (Value -> msg) -> Sub msg

index.html

app.ports.setYofElementById.subscribe(function(id) {
  var element = document.getElementById(id);
  var rect = element.getBoundingClientRect();
  app.ports.getYofElementById.send({"number": rect.top});
})

侦听器是一个订阅

subscriptions : Model -> Sub Msg
subscriptions model =
    Ports.getYofElementById getYofElementById

getYofElementById : Decode.Value -> Msg
getYofElementById value =
    let
        result =
            Decode.decodeValue bSimpleIntValueDecoder value
    in
    case result of
        Ok simpleIntValue ->
            SetSelectedElementYPosition (Just simpleIntValue.number)

        Err id ->
            SetSelectedElementYPosition Nothing

SetSelectedElementYPosition 只是设置模型。

现在,执行此操作的动作做了两件事:调用 Port.setYofElementById,然后滚动到模型中的 Y 值,假设它已经设置。

update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of

        ScrollToY idString ->
            model
                => Cmd.batch
                    [ Ports.setYofElementById (Just idString)
                    , Task.attempt (always NoOp) <| Dom.Scroll.toY "ul" model.selectedElementYPosition
                    ]

但是,这不会按顺序发生。当动作第一次触发时,什么也没有发生。如果我再次触发它,它会滚动到第一个动作中要求的位置。所以它似乎在设置值之前调用 Dom.Scroll.toY

有没有办法强制 ScrollToY 中的 Cmd 按顺序发生?或者一般来说有更好的方法来做到这一点吗?

您可以让 Cmd 按顺序执行,方法是让第二个执行 Dom.Scroll.toY 的事件作为对第一个执行 [= 的响应的响应13=]。以下更新函数完成此操作:

update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        ScrollToY idString ->
            (model, Ports.setYofElementById idString)
        SetSelectedElementYPosition (Just newY) -> 
            (model, Task.attempt (always NoOp) <| Dom.Scroll.toY "ul" newY)
        SetSelectedElementYPosition Nothing -> 
            (model, Cmd.none)
        NoOp -> 
            (model, Cmd.none)

Cmd 正确排序后,您需要确保 Dom.Scroll.toYnewY 参数在正确的参考系中以获得您想要的效果.

我终于通过将 Task.attempt (always NoOp) <| Dom.Scroll.toY "ul" model.selectedElementYPosition 的操作添加到 订阅 调用的操作而不是操作来实现它。这就是关键。

对于端口,subscribesend 操作遵循完全不同的路径,因此任何对从 js 到 elm 的 send 做出反应的操作都不会在操作中被引用从 elm 到 js。

在这种情况下,由于 SetSelectedElementYPosition 是从订阅调用的,您必须在那里设置更新:

    SetSelectedElementYPosition idString ->
       ({model | selectedElementYPosition = number }, Cmd.none)
            |> andThen update GoToSelectedElementYPosition