How can I set the focus on a Html element in Elm? I tried to set the autofocus attribute on the element and it only sets the focus on the page load.
6 Answers
The focus
function in the elm-lang/dom package is used to set focus with a Task
(without using any port
s or JavaScript).
Internally it uses requestAnimationFrame
to ensure any new DOM updates are rendered before it tries to find the DOM node to focus on.
An example use:
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

- 7,160
- 2
- 33
- 45
-
Note, this *does* work, but I found it can silently fail at unexpected times if the element is added dynamically. – AdrianoFerrari Nov 01 '16 at 14:47
-
Do you have an example where this fails? – robertjlooby Nov 01 '16 at 14:54
-
Unfortunately, I don't have a reproducible example right now. I'll post back if I find another. Overall, this is definitely the right way of doing it, so my comment should be ignored until I can find a specific case :) – AdrianoFerrari Nov 08 '16 at 17:10
-
2"setting focus can silently fail if the element is invisible." http://package.elm-lang.org/packages/elm-lang/dom/latest/Dom#focus – R D Nov 14 '17 at 12:53
-
7If using Elm 0.19, this package is now part of [elm/browser](https://package.elm-lang.org/packages/elm/browser/latest/Browser-Dom). – Ty Cobb Nov 21 '18 at 04:57
A workaround for this is to use Mutation Observers. Insert this JavaScript either in your main HTML page or in the main view of your Elm code:
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);
}
}
}
Then create HTML elements by including Html.Attributes.attribute "data-autofocus" ""
.

- 21,755
- 5
- 88
- 103
With elm/html 0.19 you can set the Html.Attrbutes autofocus
to True
input [ onInput Code, autofocus True ] []
-
This doesn't appear to work reliably, I opened an issue [here](https://github.com/elm/html/issues/186). – davetapley Feb 25 '19 at 23:09
-
-
This also needs `import Browser.Dom` in the file, example here https://package.elm-lang.org/packages/elm/core/latest/Task#attempt – Nick Jun 09 '19 at 19:41
I spent quite a bit of time exploring this recently. Unfortunately, I don't think it is possible with the existing elm-html
library. However, I came up with a hack that utilizes css animations to trigger an event and embed that in pure js.
Here is my hack in Elm using a script
node and a style
node. It is very ugly in my opinion.
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
"""
In Elm 0.19, use Browser.Dom.focus
:
import Browser.Dom as Dom
import Task
type Msg
= NoOp
focusSearchBox : Cmd Msg
focusSearchBox =
Task.attempt (\_ -> NoOp) (Dom.focus "search-box")
You can choose to ignore if focusing fails like above or do something by triggering an update message.

- 2,691
- 2
- 17
- 19
Elm 0.19's Browser.Dom.focus
is the modern solution
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 )

- 13,685
- 7
- 45
- 46

- 12,186
- 6
- 68
- 106