Managing Focus for Form Errors
00:00 Let's do the easy thing first. So the easy thing is to make it so that we autofocus on the input when we land on this page. So we'll add autofocus right here. That was it, easy. Nothing going on there, boom, autofocus, awesome. So that's good.
00:15 So now let's do the little bit harder thing next, and that is we need to add a use effect. I know, a use effect, but don't worry, this is actually exactly what use effects are intended for. Side effects on the state of the world. So the state of the world being the state of the DOM
00:33 and the focus of the elements in the DOM. So we're gonna add a use effect right here, and we've got a couple of cases to consider. We're gonna add our dependency array right there, and we don't want an infinite loop, or we don't want to run this every single time, and we'll add dependencies, don't worry about that. So what we need to do is determine
00:51 whether or not there is an error, and also we need to have access to our form, and in the case that there is no error, or there is no form, then we can just exit early. So let's do that first. Let's grab our ref, so here's our form ref, use ref,
01:10 and this will be an HTML form element. We'll initialize that to null, and then come down here and add our ref, is our form ref, ooh, form, fiorm, yeah, there we go. And then we can simply say,
01:29 here's our form l, is form ref dot current, and if there's not a form l, then we'll return. This is unlikely to ever happen. It certainly will not happen with the way that we have things structured right now, but it is possible somebody will come in and be like, if thing, then return completely different JSX.
01:48 That does happen sometimes, and in that case, this will be a problem. So I always like to do those exit earlies and listen to TypeScript when it says that it's possible that form l could be a null. So we're handling that case right there, and then the next thing we need to do is determine whether or not we're in an error state at all.
02:06 So we can say if action data dot status is error, then we're good to go. Whoops, I'm actually gonna do a negation on that, so if it's not, then we'll return. And now we're getting this quick fix for our action data. I'm gonna just pass the entire action data there
02:25 because anytime the status changes, the entire action data object will change and everything, so we'll just pass the whole action data object. Okay, great, so we've taken care of these points. Now, if the form ref current matches the query of aria-invalid true, then we wanna focus on that form.
02:44 So there are actually a couple ways that you could accomplish this. You could actually get the form element and you can check its validity state and a bunch of other ways that you could accomplish this, but I actually like the way that we're gonna be doing this here. So we're gonna say if the, whoops,
03:00 the form l matches this query, which of course needs to be in a string, then we're going to say form l dot focus. So we'll focus on the form element, but there's a problem here, and that is that form elements like divs and spans, they're not focusable
03:18 unless you add a tab index of negative one. And what that means, normally you'd add a tab index of zero, so then users can tab around. If you do anything above zero, then you're controlling the order in which things are tabbed around, and that's not something you typically do.
03:35 But adding a tab index of negative one says, this is not something that people can tab around to, but it is something that I can programmatically focus on, which can be helpful in this particular case. And because our form is associated to any of its errors,
03:52 when that happens, the user will get a focus on the form, and then the screen reader will tell them, hey, there's some ARIA described by on this thing, so you can see what those errors are, or hear what those errors are, which is exactly what we want. If it doesn't match those things,
04:10 then we're gonna get our first invalid field using form query selector. I don't like this, so thanks Copilot, but no thanks. This is lying to ourselves. So we're gonna just say, it returns what it returns, and we're looking for the first invalid element inside of our form.
04:29 And if that exists, and it's an instance of HTML element, then it will be focusable. And so that is how I like to do that. No, we're not using the generic, that's lying to ourselves. This is doing a runtime check to make sure that we're doing things properly. Okay, and then we can focus on that. So let's just test this out.
04:52 So right here, I say, boom, and boom. Actually, that was kind of cheating, because I was focused on this already. So let's come down here, we'll click on submit, and boom, I get sent to the right place. If they both have errors, then I get focused on the first one. If this one doesn't have an error, then I get focused on that one.
05:10 Now, if you'd like to take a little bit of time and add some error for the whole form as a whole, and you should see that the error message shows up, and that you focus on the form. And yeah, you know what, let's just do that really quick. It'll be easy. So we'll just come here. We'll say, form errors, this is a test.
05:30 Save that, and we'll type something in here. Actually, let's not. So this will also have an error. Then we hit submit. And now, you can't actually see it, but we are focused on the form itself. The screen reader will tell us that. So let's actually, let's turn on the screen reader and test that out. So right here, actually, I am focused on the form. You can see that. Here we go.
05:51 So let's try that again. And then I am submit. And now, I am on the form itself. And if I do the hotkey to show the more content, I'm gonna see this is a test. So really helping our screen reader users out
06:08 in that case as well, which is awesome. Just a quick review of what we did here. The first thing that we did was we add a autofocus to the input. And then we add a use effect. Well, actually, I think we also added the form ref. That's quite important here,
06:27 so that we can get a handle on the form DOM node. Then we also added a tab index to the form of a negative one so that we can focus on that form element. And then in a use effect, we check that the form L exists and that we're in an error state. And if that's the case, we look to see whether the form itself
06:46 has an ARIA invalid on it. And if it does, then we'll focus on that to help with the screen readers and navigation and everything. And I should mention also that when you have focus on that form, the next tab is gonna take you to the first input. So it's exactly the way that it should be.
07:03 And then in the case that the form is not invalid, then we're gonna look for the first invalid field. And so long as it's a focusable element, then we will focus on it. And that is how you manage focus as far as errors are concerned for a form in HTML and DOM and stuff. Like most of this stuff,
07:22 we're using React and use effect and stuff, but the concepts here, it applies regardless of what framework you're using. You'd want to manage focus for your forms. And that's how you do it in Remix and React.