Navigate list items using arrow keys

[00:00] Now for the final trick, let's see how we can navigate the results by only using the arrow keys. We'll need a couple of things. We'll need a list of references to all of our list items, I'll go inside the setup, here under results, and then const resultsRefs = ref([]), and this will be an empty array. Then we'll need to know the index of the current item or the selected item. So let's do const selectedIndex = ref(0), and the initial value will be 0. So the first item in the list will be selected by default. Let's expose these two, and then to fill in the array of refs, we'll go to our v-for, and do :ref="el => {resultsRefs[index] = el}". So, basically, as we are looping through the elements we set the resultsRefs of index to the current element in the loop.

[01:13] However, we also need to make sure we reset the list every time we load the new results, so I'll go inside the search method, and here we'll do await nextTick() because we want to reset the list on the next DOM update cycle, and then do resultsRefs.value = [], equals an empty array.

[01:38] Next up, we also need to differentiate the selected item from the other items in the list, so let's go up and add a class for it. We'll do class and if the selected index equals the current index, then we'll add bg-gray-100, otherwise nothing. Let's open the modal, and here is; our first item being selected with a bg-gray-100 class.

[02:04] Since we are here we can also listen to the mousemove event and set the selected index to the current one; so every time the mouse is moved over an item, that item's index is being set as the selected one. So if I refresh, and hover the items, this selected index value is being updated with the one we hover. Moving on we'll need to go to our dialog component and tell it to listen to the keydown event, and when that happens, call a navigateResults function. This function will check the key that has been pressed and increase or decrease the selected index number. Let's grab it, go to our setup, and we'll do const navigateResults, and we'll receive the event as parameter, and then we'll switch the event.code, and we'll have two cases. The first one is arrow down, and the second one is arrow up. Now, when we press down if we reached the end of the list, we'll need to go back up to the first item. So if the selected index value equals results value length -1, then we'll set the selected index value to 0; otherwise we'll increase the value with 1. So we'll do selected index value plus equals one.

[03:51] Let's expose the method, refresh the browser open the modal, press down, and here it is. However, if I go past this list, the item is being selected, but we need a way to scroll it into view. We can do that by grabbing the reference of the selected item; so we can go here in the navigateResults function and do resultsRefs value of the selected index value, and if that element exists, scroll it into view.

[04:30] Let's test it out. I'll refresh, open the modal, press down and here it is. If I continue pressing down and reach the end of the list, if I press down one more time, we'll restart from the top.

[04:46] Let's continue with the up key. Now, if we reach the start of the list and we press up one more time, we should go to the end of the list. So if the selected index value equals equals zero we'll do selected index value equals results value length minus one; otherwise we'll do selected index value minus equals 1. So, unless we are at the start of the list, we'll continue decreasing the selected index. So now if I go down, and then go up, and up one more time, we are sent to the end of the list.

[05:35] There's one more problem needing our attention, and once we take care of it we can move on to the form submit action. If I type something in like laravel, and press up, the cursor will go at the start of the input. Same if I press down, it will go to the end of the input. To prevent that from happening we'll add the keydown event on the input. Let's find it, here it is, we'll do @keydown and call onTermKeydown.

[06:10] Let's add this function, and what this function will do is, it will prevent the event from propagating if the press key is either arrow up or arrow down. So if and we'll have an array with arrow up and arrow down; if this array includes the event.code which is the key that was pressed, let's receive the event as parameter, event.preventDefault(). Let's expose this, refresh, type something in, press up, press down, and the cursor stays in its position, which is what we wanted.

[07:06] Finally let's go to our form element and add a submit event listener with the prevent modifier and call an onSubmit function. Let's grab this, go to our setup to define it, and what this will do is, it will check if we have a selected element and if that's the case, we'll set the window.location to its url. So we'll do, if results value of the selected index value, then we'll do window.location = results.value[selectedIndex.value].url.

[08:06] Let's expose this method, refresh, open the modal, search for something, press down, and let's say "customizing laravel sail", press enter, and here it is. Go back, press "command+k" to open the modal, press down, let's say "search media records by name", press enter, and here it is. And of course it also works if I just use the mouse. So if I press this one, here it is.

Create an account to view the code snippets of this lesson.