I have tried to achieve arrow key navigation in all 4 direction. I'm able to achieve 2 direction left
and right
using nextSibling
and previousSibling
. But not found any ways to get above and below divs. I have seen many examples for arrow key navigation on card grid layout but found not one with responsive design. When you have responsive support the number of cards in each row changes. In our case, we are having a single div with wrap to create the grid. I have seen apps like Google drive which have responsive support and arrow key navigation. Sample
If you are sure all your cards have the same width you can go with the below approach:
You can find the element above or below using their x and y coordinates, which you can get using the Element.getBoundingClientRect() , window.scrollX and window.scrollY methods.
moving forward with your sample,
Add a getRect() method to your layout component to get the x and y coords.
getRect = (id) => {
const el = document.getElementById(id);
const rect = el.getBoundingClientRect();
const x = rect.left + window.scrollX;
const y = rect.top + window.scrollY;
return { el, x, y };
};
Now add conditions for ARROW_UP
and ARROW_DOWN
keys to handleKeyPress()
method
else if (e.key === NAVIGATION_KEYS.ARROW_UP) {
const { x, y } = this.getRect(selected);
// loop back in the list and select the first element
// that is above the current selected element and has the same x coordinate
for (let i = list.indexOf(selected) - 1; i >= 0; i--) {
const { el, x: tx, y: ty } = this.getRect(list[i]);
if (x === tx && y > ty) {
this.setState({
selected: list[i]
});
el.scrollIntoView(); // scrolls the element into view, try removing it to see what it does
break;
}
}
} else if (e.key === NAVIGATION_KEYS.ARROW_DOWN) {
const { x, y } = this.getRect(selected);
// loop forward in the list and select the first element
// that is below the current selected element and has the same x coordinate
for (let i = list.indexOf(selected) + 1; i < list.length; i++) {
const { el, x: tx, y: ty } = this.getRect(list[i]);
if (x === tx && y < ty) {
this.setState({
selected: list[i]
});
el.scrollIntoView(); // scrolls the element into view, try removing it to see what it does
break;
}
}
}
if you want smooth scroll effect you can add the following css
html {
scroll-behaviour: smooth;
}
I have added comments for explanation, you can ask if you don't understand something.
here's a working example forked and extended from your sample