I am searching for a way to dynamically create a tabindex
for each element in my React application. There are multiple components containing input elements I want to create a tabindex
for and I am not sure how to coordinate them and avoid collision.
My React application is consisting of multiple tables containing input elements. At runtime the tables can be extended by the user creating additional input elements. I want to alter the direction in which the user tabs through those inputs, but I am not sure how I can generate a tabindex
for each element.
The ways I came up with so far:
tabindex
should stay below 32767 so I cant just assume huge gaps)tabindex
offset on to table and get amount of used tabindex
from React object to calculate next offset (seems to me like it would break modularity and awkward to implement)tabindex
through global state (hacky and would probably break when the table is extended)tabindex
through dom tree (no idea how to implement)Is there a tool or a common practice for tabindex
creation I am missing?
I don't know whether you have already figured out this or not. But there is a trick that you can use here.
You don't need to have a different tabindex
for each cell in the table to make the tab navigation that you want. You just need different tabindex
for each column in your table. You can repeat the same tabindex
for each cell in the same column. Because when tabindex
is the same, the browser just use HTML element order to decide the next control and it's what we need in this case. So tabindex
assignment for a table will be something like this.
1 2 3 4
1 2 3 4
1 2 3 4
1 2 3 4
This is a significant win. Because for a 10X10 table we don't need 100 different tab indexes. We can do it with just 10 indexes.
Here is a small example to demonstrate this idea with React. You can run it and see.
// Table
class Table extends React.Component {
generateRows(){
const offset = this.props.tabIndexOffSet;
const cols = this.props.cols;
const data = this.props.data;
return data.map(function(item) {
const cells = cols.map(function(colData, index) {
return (
<td key={colData.key}>
<input type="text"
value={item[colData.key]}
tabIndex={offset+index} />
</td>
);
});
return (<tr key={item.id}> {cells} </tr>);
});
}
generateHeaders() {
var cols = this.props.cols;
return (
<tr>
{
cols.map(function(colData) {
return <th key={colData.key}> {colData.label} </th>;
})
}
</tr>
);
}
render(){
const headerComponents = this.generateHeaders();
const rowComponents = this.generateRows();;
return (
<table>
<thead> {headerComponents} </thead>
<tbody> {rowComponents} </tbody>
</table>
);
}
}
// App
class App extends React.Component{
constructor(){
super();
this.state = {
cols: [
{ key: 'firstName', label: 'First Name' },
{ key: 'lastName', label: 'Last Name' }
],
data: [
{ id: 1, firstName: 'John', lastName: 'Doe' },
{ id: 2, firstName: 'Clark', lastName: 'Kent' },
{ id: 3, firstName: 'Tim', lastName: 'Walker' },
{ id: 4, firstName: 'James', lastName: 'Bond' }
]
}
}
render () {
return (
<div>
<Table
tabIndexOffSet={1}
cols={this.state.cols}
data={this.state.data}/>
<Table
tabIndexOffSet={3}
cols={this.state.cols}
data={this.state.data}/>
</div>
);
}
}
// Render
ReactDOM.render(
<App/>,
document.getElementById('container')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="container"></div>
If the number of columns is manageable with this approach, I recommend you to go with your first option by assigning different offset for each table (also illustrated in the above example).If the number of columns is fixed then it will be easy. Even it's not, you can try to keep some reasonable gaps with fixed hardcoded offsets based on your use case.
If this doesn't work in that way, the next thing you can do is trying calculating offset values from your data. I assume that you will have some sort of data which are represented by this tables, like an object array or array of arrays, probably in your application state. So you can derive offsets for tables based on these data. If you are using a state management library like Redux it will be really easy and I recommend to look at reselect which can use to compute derived state in Redux.
Hope this helps!
For a common left-to-right-down DOM related representation it could something like:
var el = document.documentElement,
rebuildIndex = function () {
document.getElementsByTagName('input').forEach(function (input, idx) {
input.setAttribute('tabindex', idx);
});
};
// Firefox, Chrome
if (support.DOMSubtreeModified) {
el.addEventListener('DOMSubtreeModified', rebuildIndex, false);
// Safari, Opera
} else {
el.addEventListener('DOMNodeInserted', rebuildIndex, false);
el.addEventListener('DOMNodeRemoved', rebuildIndex, false);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With