Lessons: 12Length: 1.4 hours

Next lesson playing in 5 seconds

  • Overview
  • Transcript

3.4 Sibling Navigation

In this lesson, we'll improve the delete functionality by automatically activating a remaining item in the list. You'll also learn how to navigate between sibling elements.

3.4 Sibling Navigation

In the previous lesson, we implemented the delete functionality. And it, of course, works. And there's a lot to say about code that works. I mean, that is probably the most important thing about software development. However, from a user experience perspective, I think that we can make this work a little bit better. Because I would expect this to automatically activate one of the remaining items whenever we delete an item. For example, if we delete the second item, I think that it should automatically activate the first item, and vice versa. If we delete the first, it should activate the second one. But then that leaves the question, what if we have a third? Because if we delete the second one, well, we have two to choose from. Which one do we pick? Well, I think that we should always try to activate the previous li element in the list. And if there is not one, then we will select the first one that's available. So let's jump right into the code, and we're going to do this work inside of deleteItem. Now, we do need to be aware that whenever we remove the activated li element, it's going to remove that from the document. And when we do so, we lose all sense of relationship for that element. Because it is no longer in the document. There's no parent node, there's no siblings, there's nothing. So we need to be able to find the sibling li elements, because that's what we have. All of these are siblings to one another. We need to be able to find that. So we need to do all of this work before we actually remove the element that we want to remove. So let's use activated. And we have a property called previousSibling. And so what this means is if we select this second li element and we delete it, then it's going to find the previousSibling. Now we might think that that's this li element that says this is the first item in the first list. But that's not the case. Because if we remember, this is hard coded HTML. And there is white space in between these li elements. And those white spaces are text nodes. So even though these texts nodes are not elements, they are still considered siblings because they are all inside of this ul element. So if we say, activated.previousSibling and then we try to use the classList to add the active class, well, we're going to end up with an error. Because the previousSibling is a text node, and it says that it cannot read property add of undefined. So classList is undefined on a text node, which makes sense because we can't apply a class to a text node. So what we have to do, then, is essentially walk the DOM. We have to go through all of the previousSiblings until we find the one that we want to use. Now that sounds tedious, and it kind of is. It also sounds difficult, and it's really not because we have this previousSibling. Everything in the document has a previousSibling property. Now it might be null, because eventually we could remove everything inside of the ul element. In which case, there is no previousSibling at all, but every object has a previousSibling. So we can just keep walking through until we end up at the top of the list. So here we have this activated element. Let's assume that it's this second item. We have previousSibling. That is the text node in between the two li elements. Then we have previousSibling, again. That should be this first element. So if we delete the second one, we can see that the first one is active. So that code works, however, let's add a few items. In fact, let's add three of them. But let's make these all different so that we can tell which ones we actually delete. So this will be the final one, and let's select the All different. Now if we delete this, we're going to see that it cannot read property add of undefined. Well, that kind of is weird, because we have code that worked with our two hard coded. Well, here's the thing, by dynamically adding these elements with JavaScript and appending them to the ul element, there is no white space in between this one, this one and this one. So our code for the selected item.previousSibling.previousSibling is actually this. We have the selected item, the previousSibling is this item, and then the previousSibling of that is the text node. Now the reason why we have a text node is because it's still there in the document. So the code that we wrote works in one circumstance but not in others. So as I said, we're gonna have to walk the document. So here's what we'll do, let's create a variable called sibling, and we're going to go ahead and assign that to the previousSibling. Then we're going to have a while loop. And for as long as sibling is not null, then we want to check the sibling. We want to see if it's an li element. And if it is, then we want to add the active class. Otherwise, we want to reassign sibling to the new previousSibling and keep going and going and going. That doesn't make a whole lot of sense speaking about it, but the code will make sense. So the first thing we're going to do is check to see if the sibling has a tag name. Because if it is a text node, it doesn't have a tag. Now there's other ways we can check to see what type of object that we are working with, but we'll just stick with this because this is essentially duck typing. If it walks like a duck, it talks like a duck, then it's a duck. Well, if this has a tag name, then it's an element. But that's not the only thing that we should check. We should also check to see what that tag name is. And let's also use the toLowerCase method so that we compare the same things. So if the sibling has a tag name, if that tag name is li, then great, we have the sibling that we expect. So then we would want to add the class of active to the classList, and we're good to go. And we need to breakout with of the loop there. However, if that's not the case, well, then we want to reassign sibling to the previousSibling. And this is going to iterate for as long as the previousSibling is not null. So with that in place, we should be able to delete the second item, and the first one is automatically activated. If we add a few other items, then we should be able to select one of these, Delete, and we can see that that is selecting the previous items. So as far as that is concerned, everything's working. However, if we delete the first item, well, we're not selecting anything because there is no previous item. So that means that instead of checking the previousSibling, we should check the nextSibling property. But this starts to get a little wonky here, so what I want to do is kind of write a function that's going to select the appropriate sibling and then work with that. That way we keep our deleteItem as clean as possible and it's also only doing one job, and that's deleting the item. So we're going to do something like this, where we will have a function called getSibling. We will pass in the activated element. And then, of course, we need to write this function. So we will have getSibling. We'll just call this argument element. And we will essentially do the same thing. Except that we will have our sibling here = element.previousSibling. And so then we loop through all of this, but instead of setting the class name, all we're doing here is just getting the appropriate elements in the document. So in this case, we will return sibling that will exit the loop, that will exit the function. So that if we get to this point, then we know that we need to go the opposite direction and we will essentially do the same thing. So we can just copy and paste the same code, but this time we're going to use the nextSibling property. And so we will make the necessary changes, which is just two changes there. And then the same thing. If we find an li element to activate, then we will return that. Otherwise, there's nothing to activate, so we will return null. That means that inside of deleteItem, we do need to make sure that we have a sibling to work with. And if we do, then we will go ahead and set the classList and add active there, and that should work. So let's go back to the browser, let's delete the first item, and the second item was activated. If we delete the second item, of course, there's nothing to activate and we don't get an error. But let's add a few items so that we can make sure that everything's working, and it looks like it is. So there we go. Not only have we implemented the ability to delete items, but we've also made the user experience a little bit better. So that we don't have to keep selecting items to delete, it will just automatically select the next item in the list.

Back to the top