Auto-focus First Input on Next Page in Multistep Form
Describe the feature
The current multi-step form example does not auto-focus the first input on next page which is a useful feature I've seen implemented in other form builders such as JotForm. I'm wondering what the recommended way to do this is with FormKit, particularly when building the form from a JSON schema.
I can easily add a handler to the next/previous step functions of the useSteps plugin but I'm not sure if A) that will be the right place to do it timing-wise since the next input may not have rendered yet or B) If there is some built-in way to get the first/next visible input given a root form node object. For the former, I would typically look to something like nextTick before calling focus but I don't think I can access that (or don't know how). For the latter, my first thought was to just pass a reference to group nodes to the next step handler and then find the first non-hidden input on the fly to call focus.
Interested to hear your thoughts! Thanks for your help.
In my dev environment I have this working in some form with the approach I described above. In the next step click handler I get the new active group node and find the first input. I then focus on that element after a brief setTimeout. Some snippets I added:
Utility in useSteps() that finds the first input of a node:
const findFirstInput = (n) => {
for (var i = 0; i < n.children.length; i++) {
const child = n.children[i]
if (child.type === 'input' || child.type === 'list') {
return child
}
const res = findFirstInput(child)
if (res) {
return res
}
}
return null
}
Then down at the end of setStep:
if (autoFocus) {
const newNode = steps[activeStep.value].node
setTimeout(function () {
const firstInput = findFirstInput(newNode)
if (!firstInput) {
return
}
const elem = document.getElementById(firstInput.context.id)
elem.focus()
}, 300);
}
Note that without setTimeout it did not work. Is there some better way than setTimeout, perhaps a particular event on the new group node that I should wait for with node.on? Just hoping there is something more precise. EDIT: I just quickly tried created, settled, and even prop events and am not seeing anything akin to "inputs are now visible for this node".
Also, is child.type === 'input' || child.type === 'list' a reasonable way to filter to nodes that accept input? Not sure how future proof that is. EDIT: I had to also exclude hidden inputs via child.context.type === 'hidden'
Thanks.
I've improved on the approach above based on this SO answer. See focusAndOpenKeyboard usage in the useSteps plugin for an example.
I still have the open question about confirming the right way to filter to nodes that accept input but I'm going to close this as I've got it ~working for now. It would probably be a useful thing to build right into formkit (or some officially supported multi-step form plugin).