如何将焦点放在 Elm 中的元素上?
How to set focus on an element in Elm?
如何在 Elm 中将焦点设置在 Html 元素上?我尝试在元素上设置 autofocus 属性,它只将焦点设置在页面加载上。
我最近花了很多时间来探索这个问题。不幸的是,我认为现有的 elm-html
库是不可能的。然而,我想出了一个 hack,利用 css 动画触发事件并将其嵌入纯 js。
这是我在 Elm 中使用 script
节点和 style
节点的 hack。在我看来很丑
import Html exposing (div, button, text, input, node)
import Html.Events exposing (onClick)
import Html.Attributes exposing (type', class)
import StartApp.Simple
main =
StartApp.Simple.start { model = model, view = view, update = update }
model = []
view address model =
-- View now starts with a <style> and <script> (hacky)
(node "style" [] [ Html.text style ]) ::
(node "script" [] [Html.text script ]) ::
(button [ onClick address AddInput ] [ text "Add Input" ]) ::
model |>
div []
type Action = AddInput
update action model =
case action of
AddInput -> (Html.p [] [input [type' "text", class "focus"] []]) :: model
-- Use pure string css (hacky)
style = """
.focus {
animation-name: set-focus;
animation-duration: 0.001s;
-webkit-animation-name: set-focus;
-webkit-animation-duration: 0.001s;
}
@-webkit-keyframes set-focus {
0% {color: #fff}
}
@keyframes set-focus {
0% {color: #fff}
}
"""
-- Cheating by embedding pure javascript... (hacky)
script = """
var insertListener = function(event){
if (event.animationName == "set-focus") {
event.target.focus();
}
}
document.addEventListener("animationstart", insertListener, false); // standard + firefox
document.addEventListener("MSAnimationStart", insertListener, false); // IE
document.addEventListener("webkitAnimationStart", insertListener, false); // Chrome + Safari
"""
解决此问题的方法是使用 Mutation Observers。在您的 HTML 主页面或 Elm 代码的主视图中插入此 JavaScript:
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
handleAutofocus(mutation.addedNodes);
});
});
var target = document.querySelector('body > div');
var config = { childList: true, subtree: true };
observer.observe(target, config);
function handleAutofocus(nodeList) {
for (var i = 0; i < nodeList.length; i++) {
var node = nodeList[i];
if (node instanceof Element && node.hasAttribute('data-autofocus')) {
node.focus();
break;
} else {
handleAutofocus(node.childNodes);
}
}
}
然后通过包含 Html.Attributes.attribute "data-autofocus" ""
.
创建 HTML 个元素
elm-lang/dom 包中的 focus
函数用于通过 Task
设置焦点(不使用任何 port
s 或 JavaScript)。
它在内部使用 requestAnimationFrame
来确保在尝试找到要关注的 DOM 节点之前呈现任何新的 DOM 更新。
使用示例:
type Msg
= FocusOn String
| FocusResult (Result Dom.Error ())
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
FocusOn id ->
( model, Dom.focus id |> Task.attempt FocusResult )
FocusResult result ->
-- handle success or failure here
case result of
Err (Dom.NotFound id) ->
-- unable to find dom 'id'
Ok () ->
-- successfully focus the dom
使用 elm/html 0.19 您可以将 Html.Attrbutes autofocus
设置为 True
input [ onInput Code, autofocus True ] []
在 Elm 0.19 中,使用 Browser.Dom.focus
:
import Browser.Dom as Dom
import Task
type Msg
= NoOp
focusSearchBox : Cmd Msg
focusSearchBox =
Task.attempt (\_ -> NoOp) (Dom.focus "search-box")
如果像上面那样对焦失败,您可以选择忽略,或者通过触发更新消息来做一些事情。
Elm 0.19 的 Browser.Dom.focus
是现代解决方案
import Browser.Dom as Dom
import Task
type Msg
= NoOp
| Focus String
focusElement : String -> Cmd Msg
focusElement htmlId =
Task.attempt (\_ -> NoOp) (Dom.focus htmlId)
update : Msg -> Model -> (Model, Cmd Msg)
update msg =
case msg of
Focus htmlId ->
( model, focusElement htmlId )
NoOp ->
( model, Cmd.none )
如何在 Elm 中将焦点设置在 Html 元素上?我尝试在元素上设置 autofocus 属性,它只将焦点设置在页面加载上。
我最近花了很多时间来探索这个问题。不幸的是,我认为现有的 elm-html
库是不可能的。然而,我想出了一个 hack,利用 css 动画触发事件并将其嵌入纯 js。
这是我在 Elm 中使用 script
节点和 style
节点的 hack。在我看来很丑
import Html exposing (div, button, text, input, node)
import Html.Events exposing (onClick)
import Html.Attributes exposing (type', class)
import StartApp.Simple
main =
StartApp.Simple.start { model = model, view = view, update = update }
model = []
view address model =
-- View now starts with a <style> and <script> (hacky)
(node "style" [] [ Html.text style ]) ::
(node "script" [] [Html.text script ]) ::
(button [ onClick address AddInput ] [ text "Add Input" ]) ::
model |>
div []
type Action = AddInput
update action model =
case action of
AddInput -> (Html.p [] [input [type' "text", class "focus"] []]) :: model
-- Use pure string css (hacky)
style = """
.focus {
animation-name: set-focus;
animation-duration: 0.001s;
-webkit-animation-name: set-focus;
-webkit-animation-duration: 0.001s;
}
@-webkit-keyframes set-focus {
0% {color: #fff}
}
@keyframes set-focus {
0% {color: #fff}
}
"""
-- Cheating by embedding pure javascript... (hacky)
script = """
var insertListener = function(event){
if (event.animationName == "set-focus") {
event.target.focus();
}
}
document.addEventListener("animationstart", insertListener, false); // standard + firefox
document.addEventListener("MSAnimationStart", insertListener, false); // IE
document.addEventListener("webkitAnimationStart", insertListener, false); // Chrome + Safari
"""
解决此问题的方法是使用 Mutation Observers。在您的 HTML 主页面或 Elm 代码的主视图中插入此 JavaScript:
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
handleAutofocus(mutation.addedNodes);
});
});
var target = document.querySelector('body > div');
var config = { childList: true, subtree: true };
observer.observe(target, config);
function handleAutofocus(nodeList) {
for (var i = 0; i < nodeList.length; i++) {
var node = nodeList[i];
if (node instanceof Element && node.hasAttribute('data-autofocus')) {
node.focus();
break;
} else {
handleAutofocus(node.childNodes);
}
}
}
然后通过包含 Html.Attributes.attribute "data-autofocus" ""
.
elm-lang/dom 包中的 focus
函数用于通过 Task
设置焦点(不使用任何 port
s 或 JavaScript)。
它在内部使用 requestAnimationFrame
来确保在尝试找到要关注的 DOM 节点之前呈现任何新的 DOM 更新。
使用示例:
type Msg
= FocusOn String
| FocusResult (Result Dom.Error ())
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
FocusOn id ->
( model, Dom.focus id |> Task.attempt FocusResult )
FocusResult result ->
-- handle success or failure here
case result of
Err (Dom.NotFound id) ->
-- unable to find dom 'id'
Ok () ->
-- successfully focus the dom
使用 elm/html 0.19 您可以将 Html.Attrbutes autofocus
设置为 True
input [ onInput Code, autofocus True ] []
在 Elm 0.19 中,使用 Browser.Dom.focus
:
import Browser.Dom as Dom
import Task
type Msg
= NoOp
focusSearchBox : Cmd Msg
focusSearchBox =
Task.attempt (\_ -> NoOp) (Dom.focus "search-box")
如果像上面那样对焦失败,您可以选择忽略,或者通过触发更新消息来做一些事情。
Elm 0.19 的 Browser.Dom.focus
是现代解决方案
import Browser.Dom as Dom
import Task
type Msg
= NoOp
| Focus String
focusElement : String -> Cmd Msg
focusElement htmlId =
Task.attempt (\_ -> NoOp) (Dom.focus htmlId)
update : Msg -> Model -> (Model, Cmd Msg)
update msg =
case msg of
Focus htmlId ->
( model, focusElement htmlId )
NoOp ->
( model, Cmd.none )