使用elm高阶函数进行键盘事件

时间:2016-09-27 12:27:18

标签: javascript-events functional-programming elm

我正在尝试创建一个更高阶的函数来创建仅捕获特定键代码的函数。该代码的灵感来自Evan" onEnter"来自他的todomvc实现的功能,它只捕获输入函数。

onKeyCode : Int -> Msg -> Attribute Msg
onKeyCode keycode msg =
    let
        captureKey code =
            if code == keycode then
                msg
            else
                NoOp
    in
        on "keydown" (Json.map captureKey keyCode)

onEnter = onKeyCode 13
onEsc =  onKeyCode 27

现在我想将它添加到查看器中的输入组件:

input
 [ class "edit"
 , id ("todo-" ++ toString item.uid)
 , value item.message
 , onInput (UpdateItem item.uid)
 , onBlur (SwitchEditTodo item.uid False)
 , onEnter (SwitchEditTodo item.uid False)
 , onEsc (UndoEditTodo item.uid)
 ]
[]

如果我只有onEnter,代码将按预期工作,但如果我添加onEsc,则永远不会执行onEnter代码。我在哪里做错了?是高阶函数上下文的问题还是" on"在单独的函数中使用多个值进行映射?

1 个答案:

答案 0 :(得分:9)

您正在向input元素添加两个onkeydown属性,并且只有其中一个可以获胜。列表中的第二个将覆盖第一个。如果在幕后使用addEventListener,情况并非如此,但现在我们可以用稍微不同的方法来解决它。

您可以编写一个onKeysDown函数,该函数接受可能的密钥代码列表以及他们应该调用的消息:

onKeysDown : List (Int, Msg) -> Attribute Msg
onKeysDown keys =
  let
    captureKey code =
      List.filterMap (\(k, m) -> if k == code then Just m else Nothing) keys
        |> List.head
        |> Maybe.withDefault NoOp
  in
    on "keydown" (Json.map captureKey keyCode)

然后你可以编写速记函数来处理这样的特定键:

enter msg = (13, msg)
esc msg = (27, msg)

现在你可以在你的视图中使用它:

input
    [ ...
    , onKeysDown
      [ enter <| SwitchEditTodo item.uid False
      , esc <| UndoEditTodo item.uid
      ]
    ]

这是有效的,因为它生成一个keydown事件处理程序属性。当你用一个预定义的元组替换一个更高阶的函数时,它仍然会给你一个可读的代码。