如何在Elm中设置焦点?
答
在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
库是不可能的。但是,我想出了一个利用css动画触发事件并将其嵌入到纯js中的黑客技术。
这里是我在Elm使用script
节点和style
节点进行破解。我认为这很丑陋。
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。插入这个JavaScript无论是在主HTML页面或在你的榆树代码的主要观点:
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元素。
注意,这确实* *工作,但我发现,如果元素被动态地添加它可以默默地在意外的时间失败。 – AdrianoFerrari
你有没有失败的例子? – robertjlooby
不幸的是,我现在没有一个可重复的例子。如果我找到另一个,我会回复。总的来说,这绝对是正确的做法,所以我的评论应该被忽略,直到我能找到一个特定的情况:) – AdrianoFerrari