Navigation Events with JavaScript
Check out a free preview of the full Professional CSS: Build a Website from Scratch course
The "Navigation Events with JavaScript" Lesson is part of the full, Professional CSS: Build a Website from Scratch course featured in this preview video. Here's what you'd learn in this lesson:
Kevin demonstrates how to use JavaScript to open and close a mobile navigation menu. He uses the `document.querySelector` method to select the button and adds an event listener to the button that listens for a click event and toggles the `area-expanded` attribute of the button.
Transcript from the "Navigation Events with JavaScript" Lesson
[00:00:00]
>> Kevin Powell: All right, so now that we've got our mobile navigation set up and we have it on our page, we need to actually be able to open and close it. And to do this, we are gonna be using a little bit of JavaScript. And along with just having to have the JavaScript open and close it, there's a few other things we need to do because we have those area labels that we were using in there.
[00:00:18]
So we have the area expanded, that we need to toggle to show if the element is actually open or closed as well. So the very first thing we need to do is actually create a JavaScript file to start writing some JavaScript. So over in my file explorer here, I can just make a new file.
[00:00:36]
We can just call it main.js if you wanted a different name, come in with the name that you're comfortable with there. And we just don't wanna forget once you create your file, cuz I've done it many times, just like with my CSS files, I forget to link it and then it doesn't work.
[00:00:50]
And I don't know why nothing is working, so we can come in with our script. The src can look at our main.js and I'm gonna throw a defer on here. Just to make sure that the JavaScript is running after the page is loaded. If you also put your script tag at the bottom, that would do the same thing.
[00:01:07]
I just like having everything in my head, it's easier to stay organized that way. And then just defer the JavaScript. So the JavaScript won't start slowing the page down as everything else is trying to come in. With that done, we need to figure out the best way to actually get our button, cuz the first thing we need to do is get that button so we can track and listen for clicks on it.
[00:01:26]
And right now, the button itself doesn't have much on there. If we go and take a look, or it doesn't have a class or an ID on there, if we take a look at it, and maybe we'll go full screen. We have the button there that just has the area controls primary nav and the area expanded a false.
[00:01:42]
You could definitely add either an ID, a class, data attribute, anything you want to be able to hook into that with the JavaScript. But I don't like adding things just for the sake of it. And again, we can sort of enforce doing it the proper, accessible way by making it.
[00:01:59]
So we need to have our area controls on there by using that as the way that we'll find the element in JavaScript. So I'm just gonna copy that area controls to make my life a little bit easier. And over here, we can say const navToggle = document.querySelector. And inside of a parentheses, you do it like you would do a regular element selector has to be in quotation marks.
[00:02:26]
Sorry, an attribute selector, so we do need the square brackets in there, and I can paste in my area controls equals primary nav. The one thing to be careful with if you're doing this is because I'm using an attribute, the attribute does have the quotation marks, with a double quote on there.
[00:02:42]
So I just need to use the single quotes out on the outside, or vice versa. If you're using double quotes on the outside, you could replace the ones on the inside with single quotation marks. Either one is fine but you just need to be consistent with how that's working.
[00:02:56]
If you're newer to JavaScript, just really quickly, the querySelector's looking at the page. It's finding the first element on the page that has this area attribute on there. It's just using a CSS selector the same way as anything and it's finding that. Now, the other thing we do wanna get is actually the navigation, as well, that would be something else we gonna have to hide and show.
[00:03:17]
So we might as well get that while we're at it. In this case, we could either get it by the class or the ID. We have the ID on there, we have class on there, it's upto you. You could get element by ID slightly faster than a query selector, but the optimization there is pretty minimal.
[00:03:33]
So you can decide which way you wanna select it. Since am using just a regular query after there, am gonna say const primary Nav is equal to. We'll do it the same way, document.query selector, and I'll do my primary navigation. Or if you want the ID again, we had that as ID primary Nav, they both would work.
[00:03:58]
With that in place now, we might as well listen for when things are being clicked on. So we can start with that by saying our nav toggle, and we want to add an event listener. And event listeners listen for all sorts of events, if you've never used one before, there's tons of different things.
[00:04:13]
You can look it up on the MDM to see all the different things. We're gonna be looking at a couple of different ones eventually, but we'll see here an event listener, and I wanna listen for a click. We can just put click like that inside quotation marks, single or double would work.
[00:04:28]
You put a comma, and then after that, you can put a function. We could have declare a function down here and just do it as a callback right here. Or we can just do an anonymous function right here. So I'm just gonna do an arrow function, so parentheses, fat arrow, and then opening, closing curly braces and hit return.
[00:04:48]
So I can do something when we click on it. So it's basically when I click do whatever is inside of this function right here, and if I always like just doing a console log of click, let's say, clicked. I just do this the same reason on my CSS files, I like doing a background on the body of purple, or something like that when I first get started, just to make sure what I've done worked.
[00:05:11]
Instead of writing a whole bunch of code and then realizing I made a silly mistake, but not knowing what it is, cuz I have 20 lines that I'm looking through. So right away, we can test to see if this is working. Because we're using a console log, you can just right click and inspect or however you like opening your dev tools.
[00:05:26]
Look inside the console, and if you don't see your console, like, if your dev tools are sort of squished off to the side, it would be in the arrow here. This is in Chrome, it might be a little bit different if you are in another browser, and you can select console from the drop down.
[00:05:40]
And, if I click there, we see clicked comes. And every time I click on it, we can see it's working, cuz I'm getting that the number just keeps going up every time I click on there, nice and simple. Of course, we don't want to just say it's clicked, we actually wanna do something.
[00:05:53]
And I mentioned the first thing we wanna do is actually change the area expanded, cuz when we click on it, it should become expanded. Or actually, let's just go look again. By default, the area expanded is set to false because it will be closed, I know we can see it right now.
[00:06:08]
But by default, it'd be closed, when I click on it, I wanna switch that to being open, so the area expanded will become true. For me, the easiest way to do that is to first, we can check whether or not, like what is it currently? So to do that, inside of here, I'm gonna do another const.
[00:06:26]
I'm gonna say is, or we're gonna assume navOpened is equal to, and then we get our navToggle. And from this we can get a getAttribute. And you can get any attribute you have, if you're using data attributes, it's actually a little bit easier cuz you have datasets you can use.
[00:06:47]
But we can get attribute and we can get our area expanded.
>> Kevin Powell: So it'll look something like that. And once again, then we can do a Console log, just so we can see what we're getting from that, nav opened. And if we go back, I'll do it, whoops, sorry.
[00:07:12]
And then if we go, take a look in the browser again, when I click on it, I did something wrong, because I'm getting a null. Nav, toggle, get attribute, area expanded. So yeah, just make sure you spell things correctly. [LAUGH] And that's copying and pasting from somewhere else is a good way to do that if you knew it was spelled properly.
[00:07:34]
So there we go, now we're always seeing that it's false because we'd had it set to false. So by default it'd be coming through and saying it false and because so we're saying nav opened is equal to false, basically, so nav opened will either be true or false.
[00:07:49]
So it just gives us an idea of where we're at, I find it's sort of an easy way to read that. So it just gives us an idea of where we're at, I find it's sort of an easy way to read that, we say if nav Have opened is equal you can either do true or false.
[00:08:04]
It's up to you. We'll say, if it's equal to false, we want to change it to something else, right? So if nav opened is equal to false, then what we can do is say that our navToggle, and instead of doing a get attribute, when we use get attribute, we're just getting.
[00:08:22]
The value of what it's equal to, you can also do a set attribute and when you do a set attribute and say area expanded because that's the one we're changing it now expanded. You have to put two different things the first one is what the attribute is. And then you also have to come through and say what you actually wanna set it to In this case, I wanna set it to true.
[00:08:44]
Then here I can come in with an else and say, we'll just copy this cuz it's a bit faster, navToggle area expanded false. So we're saying if the nav open is equal to false, we're gonna set the attribute to true. If the nav open is not equal to false, that means it is equal to true, we're switching it over, so it's one or the other.
[00:09:06]
So now I've left this console log down here at the bottom. So we should see when I click on it, it's false, then true, then false, then true, then false, then true. So it's gonna toggle back and forth every time we click on it. One thing that's really important here is, in, if you know JavaScript a little bit, you might feel like you can just do something like this, so you could check like nav opened, or do it that way.
[00:09:28]
In a lot of situations, that type of thing is the right idea, but in this situation, that doesn't work. And that's because when we're getting the value of an attribute, it's always going to be a string, so it's not actually a Boolean of true and false. It's just a piece of text that's says true and false, so we actually have to say is equal to false.
[00:09:46]
There's a way you could turn that into a Boolean if you wanted to, but for me, I just put in a simple if else statement here, I think that's completely fine. You could also fancy this up a little bit as well with a good ternary operator. But I find this really clear, it's only four lines, I think it's the easiest way, just if you're scanning through to understand what it is.
[00:10:04]
So in my opinion, I think that's the easiest way to tackle that type of problem. Now, with that done, we need to figure out how we're going to style it, to make it actually show up and hide. And there's a few different choices here that you could take, none of them are necessarily wrong.
[00:10:18]
So on here, we're already doing some work, so we could say when this is set to false, we could add a class or something else to the navigation itself. So, using JavaScript, you can dynamically add that onto the our primary nav here, and so you can have a nav, close nav, open classes.
[00:10:37]
Or something like that that you're also toggling at the same time. It would work completely, it's not gonna cause any problems, it's gonna work completely fine. So if you prefer taking that approach, you can definitely do it. Once again, though, since we're sort of in this area land and trying to enforce using the proper area attributes on everything.
[00:10:54]
I'm actually going to take advantage of how the area expanded its switching to change the styling on my navigation itself. So to be able to do that, let's jump on over to the CSS. All right, so now to style this up, the first thing we want to do is actually make it disappear.
[00:11:11]
So on my primary navigation here, where let's actually go down inside my media query cuz we want it to only be display none if we're inside the media query. We want this Navigation if we're at larger screen sizes should still show up, we'll deal with the button later on.
[00:11:27]
But when we're at the smaller size, we want the display none, and then when we click on it, we want it to come in. And we have to be careful here because we're inside of primary navigation, and then we're going inside here. So, if I wanna create another, there's a way of doing it that is nested using an ampersand, but it's kind of awkward to read.
[00:11:47]
So this is one of those times where it's probably better to come outside of that initial selector and create a new media query. It is be a little bit simpler for you to look at, but I'll show you the alternative after just in case you do prefer it, keeping it all nested together.
[00:12:01]
So, I'm gonna say @media, width is less than 760 pixels. What I'm gonna do now is I want to base the display of the primary nav looking at that area expanded. So we start with the area expanded, in area expanded is equal to true, because if it is expanded, we need to see it.
[00:12:34]
And because the way we have our DOM set up here, the HTML set up, our button is coming first and the NAV is directly after it, and like we saw when we used our plus selectors before. The plus selector is looking at what comes directly before it. So in this case, or it's not the selector, it's a combinator.
[00:12:50]
So the plus combinator right there, and then we can say.primarynavigation. And we can change that to a display of block. And now when I click on it, it will show up and when I click on it again, it will go away. So I can just make that a little bit bigger so you can see everything.
[00:13:14]
So, there's the media query, and this is a bit specific to how we've set things up, as I mentioned, just because for this to work is It has to be the button first, and then the nav right here. If you had anything else in between, let's just put an empty element here, it doesn't matter what it is, it's gonna break the functionality of that.
[00:13:33]
Because the plus combinator is looking for what's directly before it. So is the area expanded of the element directly before it true, then I'll change it to display block. If you want something that could work with a bit of some differences going on, you could switch this plus out to the tilde.
[00:13:50]
This is a general preceding sibling, so as long as any of the siblings that came before it are the area expanded of true, it will work. So then we restored our functionality there, even though we have that link in between them. Either one of them works, It depends on one of them, I guess with the tilde it's potentially a little bit safer.
[00:14:08]
So again, this is anything that comes that's a direct sibling. It won't look at other ancestors, it's only looking at direct siblings that come before it that have this, then we can toggle it. Again, we could do this with just toggling a class on and off, it's a little bit simpler in the CSS side of things.
[00:14:24]
Rather than having to come in with this kinda weird thing that we're doing right now. But this enforces us to use the proper accessible way of doing things. So if you had another team member came in and they were replicating this. The only way that it would work without them writing any extra code is to have all of the correct semantics placed on the elements they're putting on the page.
[00:14:42]
So it's a nice way to enforce things right It's right there. Now I did mention that we could do this with the nesting. So if you prefer everything being nested, I'm gonna cut this out, bring it up inside where we have all of our nesting, and switch this, and I think this is gonna work, to an ampersand.
[00:15:00]
Cuz the ampersand is the placeholder for the original selector. And so, there we go, that still works. I would say that if anybody comes across that, they're gonna look at it a little confused, this is not the most readable thing. If you do this, leave a comment in your code explaining what's going on cuz it just looks really weird.
[00:15:17]
And people will not be used to seeing this or you'll come back to your own project in a month cuz you wanna steal this to use it on another one. And you'll have no idea what this is actually either. So if you decide to do this, [LAUGH] it looks weird.
[00:15:30]
So definitely leave a comment, but I'm gonna leave it outside in its own media query, just cause I find it a lot more readable. And even if this, you feel like it does need a little bit of an explanation, leave a comment in your code to explain what it's actually doing.
[00:15:43]
All right, and next, so the last thing we need to do here is we notice when we're at larger screen sizes, our button becomes visible, which we don't want [LAUGH]. It should only be there when we're at our smaller screen sizes, this is the easiest part to fix for this.
[00:15:55]
All we want to do is when we're not inside any media queries, which is our larger screen sizes, we can just come and in this case, I don't wanna use the area expanded, cuz that toggles back and forth. But we do also have the area controls that we used in our JavaScript as well to select it, so I'm gonna do that too.
[00:16:14]
If you like or prefer having a class, by all means, throw a class on here. I don't see the point in adding a class if I don't have to, cuz I have something else that, so that I can select it with. And I'll do a display of none, so it vanishes away and I don't have a button.
[00:16:32]
It's always gonna be gone now though, but that's good at this size and not so good at smaller screen sizes. So then when we get into, or actually we'll do this all the way at that bottom media query down here, cuz it's not part of our primary navigation.
[00:16:47]
So I can bring that back in, make sure this one's not nested in that primary navigation media query, and we do a display of block.
>> Kevin Powell: And so now at the smaller screen sizes, we got our hamburger menu shows up and at larger screen sizes, it disappears. That one should is a little bit simpler on how we can get that one to work.
[00:17:12]
One question I just thought about this is, I am using a lot of attribute selectors, and I get comments sometimes with the performance of attribute selectors. There's some really old articles out there, like over ten years old, they talk about how attribute selectors are slower than class selectors.
[00:17:29]
The browsers are really optimized these days. For most selectors, you can throw at it, you won't see any difference at all in select performance between the two of them. Or if you do, it's so small, it's impossible to even notice. So yeah, don't stress about attribute selectors, don't stress about descendant selectors.
[00:17:46]
If you're using some really advanced, crazy selectors with some modern tools that are just coming out that the browsers haven't optimized for. It's the only time you could potentially run into any issues with selector performance. And in general, selector performance, again, CSS optimization, it could be something you're worried about, but I'd worry about images, JavaScript and a lot of other things first.
Learn Straight from the Experts Who Shape the Modern Web
- In-depth Courses
- Industry Leading Experts
- Learning Paths
- Live Interactive Workshops