I am toying with an idea of rewriting our current Backbone-based app in React. One piece of functionality that is giving me headache is keyboard navigation. The app must be navigable using keyboard. Here is roughly what this looks like:
There is a header element with several buttons. There is also a main area with navigable elements that is built dynamically, from the results of a network request. The elements in the main area are arranged on the screen in a sort of grid (usually, 2 rows of 3 elements each, though on the mockup I showed rows of 2 elements).
The focus is initially on Element 1 (though if no elements have been fetched, then I guess a header button should be focused). Using arrow keys, one should be able to navigate within a component (from Element 1 to Element 2 with the right arrow key; from Element 2 to Element 1 with the left arrow key; from Element 1 to Element 3 with the down arrow key, and so on), and between components (from Element 1 to the header’s Button 1 with the up arrow ket; from header’s Button 1 to Element 1 with the down key, etc.). The elements that don't fit on the screen should be brought in view with the press of an arrow button when focused on an appropriate element (e.g., pressing right arrow when focused on Element 2 should "scroll" the main area to the right and bring Element 5 into the viewport).
Currently, in the original Backbone-based app, this functionality is achieved by adding a custom attribute to all navigable elements and then using a third-party jQuery-based library that calculates the absolute positions of the elements on the screen and moves the focus from one element to the next depending on how they are positioned relative to each other. What would be an idiomatic React (+ Redux, if possible) way to reproduce this behavior?
My recommendation:
Add and event listener to the container component for the elements. Set up an event listener in componentDidMount with a callback that somehow calculates what the next element is based on what button it was (hard for me to advise on this--if you are strict about only having 4 per page with max 2 rows, then you can do something like (left) => current - 1, right (right) => current + 1, (down) => current + (count / 2), etc). That will fire an action like
{ type: ELEMENT_SELECTED, payload: { selectedElID } }
or if you're just keeping it in local state, this.setState({ selectedEl })
Then in your render function, always make sure that its rendering with the selected element in view.
Does that make sense?
I've come across with this old post today, but maybe a good solution for this kind of problem could be React-Navetree lib. It calculates distances between node to resolve who is the next/previous node, but it doesn't keep an active one.
Related
As per the react-scroll readme, we have to pass in
to="target"
props to help in tracking the element and also to navigate to the element when clicked.
We have a use-case where we need to make the link active some pixels before the element reaches the top. And, when clicked on the link, it should navigate to where the start of the element.
I tried setting
offset={-200}
to negative values. It solves the use-case of making the link active even before the element reaches the top. But, when clicked on the link, it navigates to the element with set offset, meaning, not bringing the element to the top and adding the offset pixel.
How can we customise these two behaviours? In case of click, the navigation point should be the start of element and in case of scrolling the link should become active even before the start of element is reached.
I think, if we can stop the navigate to behaviour of Link somehow then we can use onClink callback function to scroll to an element start while keeping the scrolling with mouse behaviour working with desired offset.
Please let me know if you have a way out. Thanks in advance!!
How to make an endless table scroll in React so that a maximum of 25 elements can be seen - but it scrolls not only when the mouse is inside the table, but also when the mouse is outside the table + you need to take into account that the table will not be the first element on the site and I think you can make the function dynamic tables so that the table would not be created by an extra screen position, for example, +2 elements after the bottom of the screen. I would be very grateful if something comes of this my idea
This Is more of a "theoretical" question that often buffles me in different situations and use cases, I will give a simple example to demonstrate it.
Let's say I have a list of 10 buttons.
Everrytime I click a button, a floating menu appears on top of the clicked button - there is only one menu visible for any given time.
Let's assume that I can't render this floating menu within the button component and I can only render it in the buttons parent level (meaning that this menu is sibling to those buttons).
I have 2 possible options to do that:
Keep the x,y position of the last clicked button and render the menu in this given position
Render the menu once and using "ref" to directly relocate the menu
On the one hand, the first approach seems more "Reactish". On the other hand, the possible implemention I can think of is pretty ugly (capturing the clicked item position and saving it to state which triggers defender), and further more, I am not so sure about re re rendering the whole container just because I need to move a small piece of it.
The second approach touches the DOM directly using refs. Although possible , doing DOM manipulations sometimes feel bad to me.
Is there a better approach? Which of the 2 makes more sense?
Any suggestion or thoughts will be appreciated!
Thanks
React uses whats called a virtual DOM, which is a representation of the DOM, that sits on top of the real browser DOM. Whenever you update state or a user performs an action the virtual DOM compares and checks the changes with the real DOM and then updates the UI accordingly.
So if certain DOM elements like a are not different between changes it does not get re rendered, only the DOM elements that have changed are re rendered. And if a property on a DOM element is changed, only the property is updated and the DOM element is not re rendered.
<div color="blue" />
to
<div color="red" />
The whole element is not destroyed and re created, only the property is changed.
However if the element in the host tree is different than the entire host tree is destroyed and recreated.
<div />
to
<p>
This is refereed to as reconciliation
https://reactjs.org/docs/reconciliation.html
So using refs is definitely more of a hacky solution since its more of an escape hatch and directly manipulates the DOM.
I would definitely stick with option 1, I think there is an elegant solution to the use case you described, it would involve just adding a click event listener in the componentDidMount and keeping track of the click position that way.
And also its hard to say without code but since your buttons will be the same, they will not be re rendered only the menu will.
Would recommend for further reading
https://overreacted.io/react-as-a-ui-runtime/
I have several list components in a tree-like data structure and I want to animate them in a way that when I click on an item, its children slide in from the right and at the same time the previous list slides out to the left.
I used an array for this with react-transition-group, if I add items to it, it works, but if I try to remove one from it, it fails.
I have a demo here:
https://codesandbox.io/s/k1wyqo64lo
If I uncomment line 91 path.shift() it stops animating.
How can I make them animate at the same time?
Plunker
This plunker shows an array displayed as a list using ng-repeat
Each array item has an up and down button allowing it to to change position with the array item above or below.
I'm also using angular-animateto flash each item red when it's position is changed.
But what I'd like to do is flash red the item moving up and and flash blue the item moving down.
Any suggestions welcome
I'm not an expert at angularJS, but, I do have an idea:
http://jptacek.com/2015/03/angularJS-CSS-Animation/
There are five AngularJS events
enter - DOM element is add to the DOM tree
leave - DOM element is removed from the DOM tree
move - DOM element is moved within the DOM tree
addClass - A class is added to an element
removeClass - A class is removed from an element
So, although "move" is only working on the button you are clicking, you can change your javascript to add a class to the other elements that you swap with and move.
Then you can trigger the CSS animation by using something in your css following the
[class]-[event]-[state]
model, where it can be like
.ng-addClass { ... }
.ng-addClass-active { ... }