Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React dynamic tabindex creation

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:

  • have a fixed offset for each table (but I have no idea how many inputs may be in a single table and tabindex should stay below 32767 so I cant just assume huge gaps)
  • pass 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)
  • tracking the next tabindex through global state (hacky and would probably break when the table is extended)
  • tracking the next tabindex through dom tree (no idea how to implement)

Is there a tool or a common practice for tabindex creation I am missing?

like image 845
thammi Avatar asked May 22 '17 07:05

thammi


2 Answers

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!

like image 80
Tharaka Wijebandara Avatar answered Sep 21 '22 23:09

Tharaka Wijebandara


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);
}
like image 33
FieryCat Avatar answered Sep 19 '22 23:09

FieryCat