 Hey, everybody, what's up? It's Rob Dodson. Welcome back to The AliCast Show. Today, I want to talk about managing focus in a single-page web application. So this is an interesting topic. I had a Googler reach out to me recently. They were like, hey, I'm building a single-page web app, meaning they click on a piece of navigation, and it just loads more content into the page without refreshing the browser. And they were wondering, for someone using a screen reader, what is the best way to convey, like, hey, this content has loaded. It is ready to interact with. So I wanted to put together a little example and walk through the process that I typically use when I'm doing something like this. So follow me over here on my laptop, and I've got this little site that I've created. If you've ever seen Lorem ipsum, which is just placeholder text that people oftentimes use in their designs, there's a bunch of fun Lorem ipsums out there for bacon and hipsters and zombies. And so this is a site that just loads different Lorem ipsums. So you've got hipster ipsum, bacon ipsum, zombies ipsum. And the thing that I really want to show off, though, is what this experience is like when I use my screen reader. So ideally, what's going to happen is we're going to interact with an anchor over here on the left, and then it's going to dump us into the page. The thing to note here is that we've got every page contains this anchor tag. I've snuck one inside of here. And so what I want to show off there is, as we're switching over to the page, we can quickly get down to that anchor. We don't have to tab through the rest of our navigation. So let's try that with our screen reader. VoiceOver on Chrome. Managing focus, SPA window. Manage visited link, zombies visited link, hipsters. Heading level one, hipster ipsum, main one item. Link, wolf moon, voiceover off. So what we're doing there is we're interacting with the menu. The second the new content loads, we're moving the focus into the page. And then because we're inside of the page, then, we can very quickly get down to the next interactive element that we want. We don't have to go tabbing through our entire side nav or navigating through our whole side nav, which in this site is very small. But you can imagine on a really big site with a bunch of drop downs and accordions and stuff in there. It can be really annoying to have to move through all that stuff yourself using a screen reader or using the tab key on your keyboard. So how do we do this? Well, basically what I've done here is I've created a little H1. It's kind of a big H1 in this case. That's taking up our entire header. And I want to show you in the dev tools, if I inspect this element. So the thing to note here is we've got an H1. And then it's been given a tab index of negative 1. Now, we shot an episode previously on using tab index. So I'll include that down in the show notes. You have access to that. But basically, tab index is an attribute that will opt elements into the focus order. And there's kind of three states that tab index can have. You can have a tab index of 0, meaning that the element is reachable by the keyboard. So I can press the tab key on my keyboard and it'll boom, focus it. You can have a tab index of greater than 0. You can have tab index like 1 or 5 or 100 or something. That's generally kind of an anti-pattern, because what it does is it jumps that element in front of everything else in the tab order. Usually, you don't want to actually do that, but I did want to mention it for completeness sake. And in tab index negative 1, like we're doing here, what that does is it means the element is not in the keyboard focus order. So I can't reach it pressing the tab key. But I can focus it programmatically by calling its focus method. And that's exactly what I'm doing every time the page changes. So let's go look at the code for that. And you can see kind of like a little bit, I'll walk you through sort of the structure of the page. So we've got our nav here with the side nav links, right? So zombies, hipsters, bacon, things like that. Three of my favorite things. And I've got this main element here. This is where we're dumping all the page content when it loads in. And then if we look a little bit further down the page, I've just got these template elements that contain all that page content. So here's the template for zombies, the template for hipsters, and the template for bacon. You can see our header with tab index negative 1. And so if we look at the JavaScript for our router, I'm just using a very simple router called page.js. I mean, you can use any router you want. I just picked this one because it was easy to set up. And what I'm doing is listening for any URL change that happens. So anytime you click a link, it's going to intercept that. It's going to tell me the URL. And then I just say basically, you know, let's figure out the bit of the URL after the slash. So in this case, that gives me like the page name, like just the word zombies or just the word hipsters. And I've got this little add page for path function. And all we're doing here is we're finding the template that matches that page path. So I'm saying, okay, cool. So get element by ID, passing the path and dash template. So again, you know, if someone passed in zombies or bacon, that will match this ID right here, bacon template. So then we grab our main element, we clear it out. We drop that content in there. We append child the template content. And then I'm just returning my main element, treating that like the page. And then the main thing to note here, sort of like the magic that happens is I then query selector the page and I look for that H1 with that tab index and negative one. And then I'm just calling it's focus method. And that just moves the user right into that content. And the nice thing is when you do that, the screen reader is just going to go ahead and announce that new content. Something to note, let's see, let me open my CSS. You can see it here. Here we go. All right, so right now I'm applying a focus style where I'm overriding the default outline style on that H1. So anytime you opt an element into the focus order, if it's an H1, if it's a span, whatever, once that element gets focused, you're going to see the browser's default focus style on it. And so if I comment this out, save this. Let me go back to our application. Let's see, so let's reload this. All right, and you see right away because we start off on zombies, it automatically updates the URL. So you get this blue focus ring on zombies. All right, I changed to hipster, changed to bacon, got this big focus ring. If you show this to your project manager or designer or even just like customers that might be confused by that, they're like, I'm not quite sure why you put this weird focus ring on this control. So this is one of the rare instances where I think it's okay to actually override the outline on this control because again, we're not asking the user to interact with it or anything like that. We're really just trying to direct the focus into that area. Really, we're kind of moving their focus navigation starting point, if you wanna think of it that way into that area so that the next time they hit tab and the next time they move the screen reader, it's going to go ahead and be able to interact with the rest of that content. So again, let's run through that just one more time so you can see how it works. VoiceOver on Chrome, managing, visited, blank, visited, blank, hipsters, heading level one, hipster, hipster, main one item, blank, wolf moon, voiceover off. Okay, so we've definitely made the greatest website in the world. And by the way, I will be sharing all this code on GitHub so you too can add your own ipsum to the ipsum glossary that we've built here. But yeah, this is generally how I manage focus in a single page web app. Just find a heading, slap tab next negative one on to it and put the user's focus over there. That about covers it for today. If you have any questions for me you can always leave them down below in the comments or hit me up on a social network of your choosing. As always, thank you so much for watching and I'll see you next time.