Instructor: Let's now use the power of event bubbling
to implement something called event delegation.
So what we're gonna do,
is to implement a smooth scrolling behavior
in the navigation,
so that when we click one of these links here,
it will then automatically scroll smoothly
to the corresponding section, all right?
So right now, as we click one of these links,
it actually moves there,
but of course we want this to happen smoothly.
And so let's now go ahead, and use event delegation
to implement this navigation.
And actually let's do that here at the top of the file.
So here in the beginning, I will have all the code
that is actually really related to the page,
while down here, we then have the code
that we have been using to experiment with some stuff.
All right?
So page navigation comes the right here
after the model window.
Let's grab this separator code here.
And in fact, we can also put this code here,
which was for scrolling.
So this code, we also need it active
in order to make this button here work, right?
So let's put that actually here second,
(mouse clicks)
and okay.
(mouse clicks)
And this here,
(keyboard clicks)
All right.
And these selections,
I will put all of them here at the top, all right?
So that's a little bit of a nicer organization
having just like in the last project,
all of the selection in one nice line, all right?
(mouse clicks)
Now we get this error here,
that, btnScrollTo has already been declared.
Oh, and that's because we actually wanted to cut this code.
So we no longer want it here.
(mouse clicks)
Okay.
So with is, we clean it up a little bit.
And so let's no go actually to the place in code
where we want to implement the page navigation.
Now let's actually first implement this
without using event delegation
so that you can see the problem in the approach
that we've been using so far.
And so let's now select all of these three links here.
So document.querySelector,
and actually it's gotta be querySelectorAll,
(mouse clicks)
and then as we already saw in the previous video,
they are called, nav__link, okay?
So this will return a node list,
and now we can use it for each method
in order to attach an event handler to each of the elements
that are in the node list.
So actually just as we did up here,
(mouse clicks)
right?
Okay.
So for each,
and so, our callback function here,
which gets access to the current element in a node list.
And so to each element,
let's now use the add event listener method.
So I click, and then another callback function.
So now we have callback inside of callback,
but that's not a problem at all, all right?
So here we again want access to the event,
and now let's just start by logging to console, link,
just like this.
So indeed we get link here, and on this one,
and also on this one.
So a three times link.
Now you already start to see the problem,
which is that by default clicking one of these links,
will scroll to the position in the HTML.
And that's because of these so-called anchors.
Let me show it to you in the HTML.
And so these here are called anchors.
So hash, and then some name here,
will automatically move to page two of the element,
which has the ID of this.
So let's see, down here we have,
yeah, here we have this section with the ID section two.
And so as we click here then,
it will automatically move to exactly that section.
And we can see it here also in the URL bar, right?
So we basically need to prevent that from happening.
Let me just put this back here,
because in our last lecture we deleted this section one
that was here.
And so, as I was saying,
we need to prevent these default behavior.
And so for that,
we use once more event or E in this case,
dot preventDefault.
And so let's see now,
and indeed now nothing happens.
So we no longer scroll to the page,
and you see that the URL bar
is actually also no longer changing as we click here.
So that problem is fixed,
and so now let's implement the smooth navigation,
or the smooth scrolling, actually.
Because if we didn't want the scrolling to be smooth,
then we actually wouldn't need any of this,
because we already saw that the browser
will actually automatically move to that place.
So to that anchor that we defined in HTML.
Now, that anchor, however,
so basically this part here is still gonna be very useful
because we can now take this href attribute.
So basically we can take this value here,
and select the element that we want to scroll to,
based on this.
Because if we think about this,
this actually looks pretty much like a selector
for an ID, right?
So let me write the code here,
and then I will explain a little bit more.
So let's get that href actually, and I'm gonna call it ID.
So that is at This, which is the current element remember,
dot getAttribute, and then the href.
Okay?
And I used the getAttribute function here
because as we learned before,
I actually only want that part that we literally write here.
So I don't want the absolute URL,
and so that's why I cannot write simply this.href.
Remember that.
Anyway, let's now log this to the console
to see again what we're doing here.
And so, you see we get now a section one here,
two, and three.
And now, as I was saying before,
this here looks pretty much like a selector already.
And so we can now take this, and select an element,
based on this, and then simply scroll to that element.
So for that,
we're gonna use the scrollIntoView method here again.
So let me show that to you.
So document.querySelector,
and now all I need to pass in, is this ID, right?
Because again, this exact string
that we get from the href attribute,
is already the perfect selector.
So we can just use that.
Okay?
And so this is a pretty common technique
that we use for implementing a navigation like this.
So we put the ID of the elements that we want to scroll to
here in the href attribute,
so that then in the JavaScript,
we can read that,
so we can read that href, and then we can select the element
that we want to scroll to.
So in this case, is the element with the ID, section one.
And that is exactly this one here.
(mouse clicks)
So exactly the first section.
All right?
So here, we have the element now selected,
and now all we need is to call, scrollIntoView,
and then with behavior set to smooth.
Just like we did already here before, okay?
And that's actually already it.
So, that worked beautifully, right?
All right, and now the third one, and here we go.
Well, that didn't really scroll
to where I thought it should go.
Now, but now it did.
Apparently something weird happened there,
but that's not a problem.
Now, as you see, this actually works just fine,
but the problem is that it's not really efficient.
So we are adding here the exact same callback function,
so this event handler here,
we are adding it once to each of these three elements.
So the exact same function is now attached
to these three elements.
And that's kind of unnecessary.
I mean, of course it would be fine for only three elements,
but what if we had 1000, or like 10,000 elements?
If we were to attach an event handler
to 10,000 elements like this,
so like we did here with the forEach function,
then we would effectively be creating 10,000 copies
of this same function here.
And so that would then certainly impact the performance.
And it's really just not a clean solution in that case.
And so, the better solution without a doubt,
is to use events delegation.
So in event delegation,
we use the fact that events bubble up.
And we do that by putting the eventListener
on a common parent of all the elements
that we are interested in.
And so in our example,
it's this container that's around all of these links,
and that we saw in the previous video.
So remember, that is this element here.
So we will put our event handler on this element here,
and then when a user clicks one of the links,
the event is generated, and bubbles up,
just as we saw in the last video.
And then we can basically catch that event
in this common parent element, and handle it there.
Because we also know where the event actually originated.
Right?
So we can figure that out
by looking at the events.target property.
So that's what event delegation is,
and so let's now go ahead and implement it.
All right, so in event delegation,
we basically need two steps.
First, we add the event listener to a common parent element
of all the elements that we're interested in.
So let me actually write it here,
because this is a really important technique.
(keyboard clicks)
So as I said, add event listener to common parent element.
And then in that event listener,
determine what element originated the event.
So that we can then work with that element
where the event was actually created.
So, let's do that.
Document.querySelector,
and so remember the common element of all these links,
is nav__links.
So again, it's this,
yeah, it's this element.
And so this is where we add the event listener,
(keyboard clicks)
and now we just need to figure out
where the event actually happened.
And remember that, that is stored in event.target, right?
So event.target becomes really, really useful now
in this strategy,
because we can now use this information
exactly to basically see where that event happened.
So as I click here, we can now see,
well, that the event occurred from this element.
And as we click here, then we see it originated of course,
from this one.
Okay?
And as we click somewhere here in the middle,
then we see that the click of course happened
in this entire element, not in one of the links.
And this part is actually important to notice
because now we actually only want to work
with the clicks that happened on one of the links.
So only this one, or this one,
but the click that happens here on this nav__links element,
is not relevant at all.
And so now we need a matching strategy here
in order to match only the elements
that we are actually interested in now, right?
And in this case, the best way to do that,
is to simply check if the target has this nav__link class.
Now, right?
So this is the matching strategy.
And this can sometimes be one of the hardest things
to come up, when you use this technique.
(keyboard clicks)
But many times, I actually do it like this.
So I check if the target element contains the class
that we are interested in.
So classList,
and then remember we can use the contains method,
and then we can simply check
if it contains the class nav__link.
And so that's then this one here, right?
And only if it does, then let's log link.
So let's see.
So as we click, we get link, and you see,
or another one, and we also get link.
But then as we click somewhere else,
like here in the middle, then we do not get link.
Now, right?
And so now we successfully
basically only selected the link elements itself.
And so now we can basically do exactly
what we did here before.
So we actually also need to prevent the default,
and so let's get all of this,
and replace that here.
Get rid of the comments, and this one here,
so preventing the default,
we actually always want to want that,
or to do that.
So no matter where we are clicking,
nothing should happen now, right?
So, let's try that.
Now, and here we get this error,
and we had null, on line 87.
So that's this now.
And of course, we still have a problem here
because the href attribute is now not on this,
but it is on the element that was clicked.
And so that is again e.target.
So that's the whole reason why we're using e.target
in the first place.
So let's try now, and beautiful.
Now it actually works.
So you see, we still get the same sections here
also locked to the console,
and it scrolls to exactly where we want it to go.
(mouse clicks)
Awesome.
So this works beautifully,
and yeah, we successfully implemented event delegation,
which is a lot better, and a lot more efficient
than simply attaching the same event handler
to multiple elements.
Instead, we simply edit one big event handler function
to the parent element of all the elements
that we're interested in,
and then we simply determined
where the click event came from.
And then we also needed this matching strategy
because we wanted to basically ignore clicks
that did not happen right on one of these links.
And coming up with this matching strategy,
as I like to call it,
is probably the hardest part
of implementing event delegation.
But don't worry because there will be plenty of examples
throughout the rest of the course.
And so, at some point,
it will all make a lot of sense to you.
So I hope that you're convinced that event delegation
is a much better strategy,
even though it requires a little bit more work
than the first implementation that we did.
And in fact,
there is actually an even more important use case
of event delegation,
which is when we are working with elements
that are not yet on the page on runtime.
So by the time the page loads.
And a great example are buttons
that are added dynamically while using the application.
So it's not possible to add event handlers
on two elements that do not exist,
but we will still be able to handle events
on elements that don't exist at the beginning
by using event delegation one more time.
And we will actually do this later in this section.