Instructor: Hello there and welcome back
to implementing our last effect,
which is also the coolest effect of all.
And this one is going to be a so-called sticky navigation.
So, as I had mentioned by the end of the last video,
a sticky navigation bar is basically
that this navigation here stays on top
as we scroll across our entire page.
So let's say we are here, or really anywhere on the page,
and then we always have our navigation bar here
nicely sitting at the top.
And once more, I'm pretty sure that you have seen
a navigation bar like this many, many times before,
because it is actually really helpful
to have that on our page.
All right, now let's start by actually
creating the style for that,
because the JavaScript part is basically
only then adding and removing that style.
So basically similar to what we did before,
also with the mobile navigation.
All right, so again,
I'm gonna start by creating a new class,
which we will then style to make the navigation sticky.
So, let's do that, in the style.css file,
which is where our navigation should be.
So here we have the mobile navigation
and now let's add the sticky navigation.
So let's call it sticky, which makes sense.
And so, now there is a new type of value
that we can specify for position.
So that's something that we hadn't done before.
So all we did until this point was
position relative and position absolute,
but now what we actually want is position fixed.
And so this will then fix this element,
basically in the view port,
and will not move it as we scroll.
So let's put it at top zero and bottom zero.
And also let's make sure that it will fill
the entire view port.
And just so we see it,
let's give it a white background color.
And let's see what happens.
Well, of course nothing happened,
because we didn't even add this here anywhere.
So let's add this actually to the entire header,
because, of course, the header is the entire part
which contains the logo and also the navigation.
So right now all of that,
so that entire bar should stay at the top.
Right, and there we can already see it.
We just reload. And so, there it is.
So this is kind of already what we are looking for.
There is just this problem that
we gave this images here a higher Z index value.
So let's inspect that.
So,
well maybe, this entire thing,
or maybe just to one of these.
Well, in any way,
what we need to fix here is the Z index value
of this entire header, so of the sticky class, right?
So let's again add some really high value here.
So Z index,
and remember how we used this one here for the button.
Now the button we will want to always
stay on top of everything.
And so let's just do 999 for this one here.
All right.
Also, we can use the same background color
with some transparency that we used
for the mobile navigation.
So that's in the queries.
So yeah, just this one here.
Okay, give it a save,
and beautiful.
So that looks really, really nice,
but we can see that some space has been lost here before.
So, that is because the header, so this entire element,
has been taken out of the flow
just like with position absolute.
So remember that when we position an element
with position absolute,
it is taken out of the flow of the page,
and therefore, it is as if it doesn't even exist.
And so then the rest of the element
fill the remaining space.
And so with position fixed, the exact same thing happens.
And so now, basically the page starts here at this element,
so at the hero section.
So you see that it now starts right there at the top.
So we will have to fix that at some point,
but for now let's actually leave it like this
so that you can later see the problem that it causes.
Now, I also want to add some shadow here,
and we also need to make this a bit smaller.
So like this, it takes up a little bit too big
of the screen I would say.
So we need to make all of this smaller.
And actually, I want to give
this navigation bar a fixed height.
So right here, so let's say 80 pixels, so eight rem,
and then we need to remove the padding
from the top and the bottom.
So we can say padding top zero,
and padding bottom zero, as well.
And so with this, the left and right,
they simply stay unchanged, okay?
And I will tell you a bit later in the JavaScript part
why it is important to have this fixed height, okay?
But for now, let's add that box shadow
that I was talking about,
and something just very, very subtle.
So horizontally as always, nothing.
Then here, just 1.2 rem and then the spread.
Let's give it like 3.2.
And then here, or black color,
can be something really subtle,
and even less than this, let's say just 3%.
Now, okay, that looks really nice.
So now it's not too high anymore, I would say,
and it still is everything vertically centered here,
because of flex box.
But of course we don't always want
this navigation to be sticky like this.
So we want this to start, basically,
after this hero section, right?
So basically, when we start the content itself,
then that's a good time to make it sticky.
So basically right here,
after we leave the hero section behind.
So once that scrolls out of the view port, right?
And so now, we can use JavaScript to do exactly that.
So basically, to add the sticky class
as soon as the hero section is no longer visible.
Alright? So let's do that. Remove it from here.
And this is going to be the most tricky part
of all that we're going to do here.
And so from now on, if you want,
you can just skip the rest of this video
and simply grab the code from the final files
of this section, now right?
But again, if you're interested in how this works,
then just stick around just like the sticky navigation
(laughing)
Now, okay?
And the way we will do this is by using a very modern way,
which is called the intersection observer.
So there would be multiple ways of doing this,
but this is the most performant way,
and also the most modern, and the best way, I would say.
So intersection observer.
And so here we now need to specify two things.
First, what we want to happen.
So here we will define a function.
And then second, some options here.
But I will not yet specify these two things.
So not the function and not the options.
But instead, I will start by saving
this new intersection observer into a variable.
So calling this one here, the observer, simply,
because now we need to actually do observer.observe.
So this sounds very confusing, I know,
but just bear with me here.
So we just need to write a little bit of code
and then things will make more sense.
Okay? So this one here could have any other name.
Let me call it obs, maybe as an abbreviation,
just to make it less confusing with the obs.
Okay? So here we now need to basically observe some element
in the HTML using this observer that we specify here.
And so the element that we want to observe,
is in this case, the hero section.
So section hero,
and this is the section that we want to observe
because we will want to make the navigation sticky
as soon as this section here moves out of the view port.
So that's what we also said before, right?
So basically as soon as this hero section here
moves out of the view port is when we want to make
the navigation sticky.
And so that's why that is the element
that we're going to observe.
Alright? So let me select that.
And create that hero first, maybe.
So section hero, element,
and then, document.
So as always, just selecting it using
document.query selector, okay?
And so this is the element that we want to observe.
So section hero. So this was the first part.
So using this observer here to observe a certain element,
which in this case is the section.
And now we need to define the options here.
So for that, we have this so-called object here.
And the first thing is the root.
And the root is basically simply,
where this element should be appearing or not.
So that sounds confusing, but it's really not.
So we're just setting it to null here.
And what that means is that we will observe
this hero section inside of the view port.
So basically, as it moves through the view port, alright?
So usually that's always what we do here.
But we could also observe some elements basically,
scrolling inside of something else.
But this is most helpful just in the view port.
So basically, inside of the entire browser window.
Now, okay, next we need to set a threshold.
And this one we set at zero.
And zero basically means that this will fire.
So basically it means that we will have like an event
as soon as 0% of the hero section
is inside of the view port.
So again, a little bit confusing, I know.
And here we could actually specify multiple values.
So it could also be one
and then we would get the event
whenever the hero section is completely
inside of the view port.
And so that's why we set null here.
So null means the view port.
But again, we want it to be zero,
because we want to get an event as soon as the hero section
moves out completely of the view port.
So again, that is this event right here.
So this situation. Okay?
And for now that is it.
And so now we need to actually specify
what we want to happen when this is the case.
So whenever we have the threshold of zero in the view port.
Okay, let's move this into another line here,
and this one as well.
And here we get access to basically,
something called an array of entries.
And there's gonna be one entries for each threshold value.
But here we just have one and so,
we can just say that constant is equal
the very first entry.
And so that is zero.
So this is again, something called an array,
which is a collection of multiple elements,
but this one has just one.
And so, the element number one is called the number zero,
in fact in JavaScript.
And so here we are taking that out
and storing it into a new variable.
And so now, to start working in this function,
let's take a look actually at this entry here.
So in the console basically.
And so, if we can see something in the console,
then it means that our main code is already working.
So especially these options that we specified here.
Okay, so let's come to the console,
and let's reload.
But I can already see something happening here.
So here we get now the intersection ratio,
which is basically 62%.
All right, and we get is intersecting.
So these are the two important values.
So this first event here always happens no matter what,
but now let's wait for it.
and I will actually clear the output here.
And so now as soon as we move out,
so as soon as this section here moves out of the view port,
we should get a new event there, right?
So let's wait for it. And there it is.
So now the intersection ratio is zero,
and is intersecting is false.
All right, and so, we now need to test for this basically.
So we will want this is intersecting
to be equal to false.
Okay, so let's write that.
So if ent, which is that event that we just saw.
So it stands here for entry dot is Intersecting
is equal to false,
then we want to add our sticky class.
So let's select the header here,
and I will do it here all in one go this time,
because actually, we will have to change this in a minute.
Yeah, right here.
So document.querySelector,
so dot header.
And so before we always did it in a separate variable.
So we stored this into a variable.
And then on that variable we did the dot, right?
But we can do it all in one go.
So all directly.
And so, let's grab the class list,
and then we want to add this sticky class here.
So this time we are not using toggle,
but instead simply adding it.
So just called sticky.
Now there is a better way of writing this.
So instead of this, we would usually just this.
All right, but I will actually leave it like this to
make the code a little bit easier for you to understand.
All right?
And so this would actually,
let's reload here, once again.
And, well,
nothing happened.
So well, let's...
So let's add these curly braces here
to create a block of code so we can have multiple lines.
And so I will only lock this event here.
So this entry when this condition here is true.
So in other words,
when ent.intersection or is intersecting is false.
And actually, I think that I did this wrong here.
Is intersecting, so I think this is the correct name.
Oh, and now it is actually already working.
So let's see.
So right now, nothing is happening.
And actually let's move this back out.
Okay, so I did this here
because I thought there was a bigger problem.
All right, so here we already have our first entry,
but it has this percentage and it is intersecting.
And so now the sticky class is not yet being added.
But let's wait for it.
And there it is.
Beautiful. Okay.
Now of course we don't have yet a way of removing it,
but we will take care of that in a minute.
Now what matters here is that we have this big jump.
So what closely what happens as the sticky class is added.
So you'll notice a big jump here in the page.
So you saw that.
So we basically skipped this entire part here.
And the reason for that is, basically,
what I explained to you before,
which is the fact that this entire header here
has now been taken out of the flow
because it is now absolutely positioned, okay?
And so we need to fix that
by basically compensating for this lost height
and add some margin to the top of this header.
So of this hero section actually.
So here we now basically need to add exactly the same margin
as is the original height of the header.
So let's reload and let's get that height.
So I will inspect it here.
And you see that, by default it is 96 pixels.
So that's 9.6 rem. All right?
And so now whenever the header class has been added.,
we want to add some margin to this one here.
So a margin top to the section hero. All right?
So let's do that here, right here.
And so how can we actually do that?
So let's first write the code here.
But of course it will not work yet.
So margin top of 9.6 rem.
But how does this relate now to the sticky class?
I mean, we only want this to happen whenever,
basically the header has the sticky class, right?
So,
let's make this here smaller.
Right?
So again, this style here should basically only apply
whenever sticky is right here on the header, right?
However, that is not possible because the header
is not a parent of section hero, all right?
And so instead what we will have to do
is to always add the sticky class,
not to the header but to the body, all right?
And so then we can do this.
So, we will then apply all these styles,
actually to the header,
but only if the sticky class is a parent of it.
So just like this.
And so then, this is exactly what we did
multiple times before.
And in particular with the mobile navigation, right?
So here we had this code where this style only applied
to the main nav if the nav open class was present
on one of the parent elements.
And so here we are doing the same.
So we will apply all these styles here to the header
if sticky is one of its parent elements.
And so that will then be the body.
And so here, we can then do the same, all right?
And finally, then, in our JavaScript,
we also need to add the sticky,
not to the header, but to the body.
So that we do by saying, document.body and yeah, that's it.
So just like this.
And let's reload manually here.
And let's see if there is the same jump now,
but it shouldn't.
And indeed we fixed that.
So beautiful.
You saw the sticky class being added here.
And so, it then should have added,
well, it's added all these styles here to the header,
and then to the here to the section hero,
it will have added that margin, right?
So that margin top of 9.6.
All right, so beautiful.
And of course if we take that sticky class away,
then we go back to normal.
So we are almost done here.
Just two more things.
So first, let's take a look again at our effect.
So you see, or you will see, now, that of course,
this became sticky exactly as we moved out of
the hero section, right?
Now the problem with that is,
that it will then overlap this first section here.
And so that means basically,
that this happens a little bit too late.
So actually, we want it to happen kind of like, here.
So exactly here.
All right, so we can do that in our code.
So here, we can add a third option,
which is the root margin.
And so that margin is then basically,
applied outside of this root.
So here we can say minus 80 pixels.
So here it has to be pixels,
it doesn't work to use percentages or rem.
And it needs to be a string like this.
So inside of quotes.
And so here, we use these 80 pixels because
that's exactly what we set the
height of the navigation bar to.
So these eight ram here are basically 80 pixels.
And so this is the reason why it was important
to set the height here manually.
So that then right here, we could use that 80 pixels.
And so, let's reload just to make sure,
with JavaScript sometimes it's better to reload.
And so now it should happen.
Yeah, exactly as we reach that. You saw it?
So, that is now almost it.
And so now the only missing part is to remove it
as soon as the hero is still visible.
So once we enter this again, we should get another event.
So, and I'll exit it, the hero again.
But now as we enter it, we will get a new event.
And so now, is intersecting is back to being true, okay?
And so we can use that to then remove it.
So we can say it's true, then, remove.
Now okay? And now here, this true is not necessary at all,
because basically this condition here,
as soon as it is true, then this here will get executed.
So this is not necessary.
And that's also the reason why here, we could set this.
Because if this is false,
then this one here will basically switch from false to true.
And so therefore, then this code here can execute.
Now, right? But again, I will leave it here like this
for you to understand the code a little bit better.
But anyway, again, reloading here.
There it is. And there it leaves.
And of course now it is back up.
So, let's see that also in the code.
So here, so now it is sticky,
and now it's gone, and it's back, and it's gone.
So working really nice, let's just try it also in Safari,
because things in Safari tend to work a little bit worse.
But still, it is working perfectly.
Very, very nice.
So, as I said in the beginning,
I know that this code here is quite tricky,
but hopefully you were at least able to follow the logic
of what I explained you here throughout this lecture.
Because if you did that,
then you might be able to adapt this
in the future to your own page.
And that is really the main goal that I have
with these few videos here.
All right, but enough talk, because it works great.
And so in the next video,
we will create our last piece of JavaScript,
or actually, we will just use what we already have here,
because we now need to fix the flex box gap property
in Safari.