How to set focus on an element in Elm?

前端 未结 5 1486
暗喜
暗喜 2020-12-14 00:32

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.

相关标签:
5条回答
  • 2020-12-14 00:42

    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
    """
    
    0 讨论(0)
  • 2020-12-14 00:42

    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.

    0 讨论(0)
  • 2020-12-14 00:44

    With elm/html 0.19 you can set the Html.Attrbutes autofocus to True

    input [ onInput Code, autofocus True ] []
    
    0 讨论(0)
  • 2020-12-14 00:47

    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" "".

    0 讨论(0)
  • 2020-12-14 00:58

    The focus function in the elm-lang/dom package is used to set focus with a Task (without using any ports 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
    

    Full example on Ellie

    0 讨论(0)
提交回复
热议问题