receives its focused argument from this other function
getActiveElement(){
if(document.activeElement === document.body){
return this.activeElement || document.activeElement
} else {
// document.activeElement can be null in Internet Explorer 11
return document.activeElement || document.body;
}
}
which to me means that the browser first has to autofocus the element to assign the document.activeElement attribute before LiveView takes over.
So it works on the fresh load because the browser renders the page in the disconnected state and autofocuses for you, but when moving between views the diffing algorithm won’t programmatically autofocus the element with the autofocus attribute.
Perhaps set an AutoFocus hook on the element, where you call this.el.focus() in the mounted() callback.
My root layout has a phx-hook="focus_by_id" on it, so I can query my whole view but you could attach with a thinner domspace if you wanted.
It’s quite useful. Lets me focus on “page load” obviously, but also direct the focus depending on server states (validation, showing a popup, “focus next step”, whatever).
I did have a more direct “focus_this_el” hook, but I found I needed the more general DOM query hook too so I just replaced it with that. I do include a push_event in the live view mount to drive initial load state.
That’s a nice strategy for directing the focus around, as long as one remembers to push the event while changing the state. It would prevent race conditions when updating a view with multiple inputs each trying to run the hook individually. Might confuse the user!
You could even generalise the feature to query by any valid selector instead of adding the “#” in the callback.
It’s the way that Phoenix.HTML seems to be going, selecting by element name instead of id. So you could send a selector string like form#myform input[name="model[name]"] to select the form field if it didn’t have an id, or send the #id complete with # part, or focus any other element, like navbar > button.className:last-child
Don’t ask me how I know this, but auto focusing an element on load may impact form value restoration (say, when reconnecting a disconnected LV).
Wrapping the call to focus in a small timeout (~5-10ms) is enough to fix that. There may be a specific event you could hook into (phx-liveview-did-paint or something, I haven’t looked yet, or you could attach your hook to updated() maybe, depends on your implementation and where you are applying the hook).
I think this is LV’s JS intentionally not clobbering a field you might have edited (I know it wants to treat the form as a source of truth) or maybe it’s just a browser thing.