最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - React dynamic tabindex creation - Stack Overflow

programmeradmin5浏览0评论

I am searching for a way to dynamically create a tabindex for each element in my React application. There are multiple ponents 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 mon practice for tabindex creation I am missing?

I am searching for a way to dynamically create a tabindex for each element in my React application. There are multiple ponents 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 mon practice for tabindex creation I am missing?

Share Improve this question asked May 22, 2017 at 7:28 thammithammi 4541 gold badge5 silver badges17 bronze badges 3
  • 1 i think this valid problem needs to be addressed diferrently - some interesting ideas in web ponents – Ovidiu Dolha Commented May 25, 2017 at 13:08
  • Generally, the best practice is to avoid using positive integers for tabindex. Can you give some context on what does you mean by "alter the direction in which the user tabs through those inputs". That probably help us to give you a better answer. – Tharaka Wijebandara Commented May 26, 2017 at 6:08
  • I have multiple tables and would like tabbing to traverse them through columns first (top to bottom) and then left to right to the next columnt after the last row is reached. – thammi Commented May 26, 2017 at 14:18
Add a ment  | 

3 Answers 3

Reset to default 7 +100

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./ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare./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 remend 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 remend to look at reselect which can use to pute derived state in Redux.

Hope this helps!

For a mon 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);
}

Here is a function that you could call every time new inputs are added to your layout. It assigns a tabIndex to each input, without any gap, and acmodates tables of various sizes, where each cell can have any number of input elements. You can test it in this jsfiddle.

The input elements are stored in a Map object, where each key is a bination of table, column, and row indices. The keys are then sorted, and the tabIndex property is set in the order of the sorted keys.

function setTabIndices() {
    var tableIndex, rowIndex, colIndex, inputIndex;
    var table, row, cell, inputs;
    var map = new Map();
    var tables = document.getElementsByTagName("table");
    for (tableIndex = 0; tableIndex < tables.length; tableIndex++) {
        table = tables[tableIndex];
        for (rowIndex = 0; rowIndex < table.rows.length; rowIndex++) {
            row = table.rows[rowIndex];
            for (colIndex = 0; colIndex < row.cells.length; colIndex++) {
                cell = row.cells[colIndex];
                inputs = cell.getElementsByTagName("input");
                for (inputIndex = 0; inputIndex < inputs.length; inputIndex++) {
                    // Define key based on table, column, and row indices
                    map.set(format(tableIndex, 4) + format(colIndex, 6) +
                             format(rowIndex, 6) + format(inputIndex, 3), 
                             inputs[inputIndex]);
                }
            }
        }
    }
    var input;
    var sortedKeys = [...map.keys()].sort(); // Not supported in IE11
    for (var tabIndex = 1; tabIndex <= sortedKeys.length; tabIndex++) {
        input = map.get(sortedKeys[tabIndex - 1]);
        input.tabIndex = tabIndex;
    }
}

function format(value, digits) {
    return ("0000000000" + value.toString()).slice(-digits);
}


Note: the following line causes trouble in IE, which does not support the spread syntax:

var sortedKeys = [...map.keys()].sort();

If you must support IE, you can call map.forEach to populate the unsorted array, as shown in this modified jsfiddle.

发布评论

评论列表(0)

  1. 暂无评论