Skip to content

Question

How do I switch "focus" to a particular HTML element?

HTML autofocus

Perhaps you can use the autofocus HTML element attribute like this:

(defn view 
  []
  [:input {:type "text" :id "my-id" :auto-focus true])

But this might not work in Safari these days (Safari is the new IE 6 of browsers).

Instead, you could use a more portable (but more complicated) version of this approach, which uses React refs with a Form-3 component:

(defn my-input []
  (let [ref (atom nil)]
    (r/create-class
     {:component-did-mount
      (fn [_]
        (.focus @ref))
      :reagent-render
      (fn [_]
        [:input {:ref #(reset! ref %)}])})))

A terse way of achiving the same outcome is:

[:input {:ref #(when % (.focus %)}]

But all these approaches only cause focus once, when the widget is first rendered. You may need to have more control than that.

Reagent after-render

If you want to switch focus between elements after they have first rendered, you can create an effect handler which uses Reagent's after-render API to register a function that will imperatively set focus:

(re-frame.core/reg-fx 
  :focus-to-element
  (fn [element-id] 
    (reagent/after-render  #(some-> js/document (.getElementById element-id) .focus))))

WARNING: as written, this code will fail silently if element-id is not found. If you use this code fragment, you may want to detect and report that problem.

You can then use this effect within your event handler:

(re-frame.core/reg-event-fx
  :something
  (fn [cofx event]
    {:db ....
     :focus-to-element some-element-id}))

This assumes you can compute or obtain the some-element-id value for the HTML element on which you want focus.

One small trick: we perform the imperative focus using Reagent/after-render because sometimes the target HTML element won't exist in the DOM until after the rendering which occurs in the next animation frame.