反应动态tabindex创建

时间:2017-05-22 07:28:38

标签: javascript html reactjs tabindex

我正在寻找一种为我的React应用程序中的每个元素动态创建tabindex的方法。有多个组件包含我想要创建tabindex的输入元素,我不知道如何协调它们并避免碰撞。

My React应用程序由包含输入元素的多个表组成。在运行时,用户可以通过创建其他输入元素来扩展表。我想改变用户选中这些输入的方向,但我不确定如何为每个元素生成tabindex

到目前为止我提出的方法:

  • 每个表有一个固定的偏移量(但我不知道单个表中可能有多少输入,tabindex应该保持在32767以下,所以我不能假设巨大的差距)
  • tabindex偏移量传递给表,并从React对象获取使用量tabindex以计算下一个偏移量(在我看来它会破坏模块化并且难以实现)
  • 跟踪下一个tabindex到全局状态(hacky并且在扩展表时可能会中断)
  • 跟踪下一个tabindex到dom树(不知道如何实施)

我缺少tabindex创作的工具或常用做法吗?

3 个答案:

答案 0 :(得分:4)

我不知道你是否已经弄清楚了这一点。但是你可以在这里使用一些技巧。

您不需要为表格中的每个单元格设置不同的tabindex即可进行所需的标签导航。您只需要为表格中的每一列添加不同的tabindex。您可以为同一列中的每个单元格重复相同的tabindex。因为当tabindex相同时,浏览器只使用HTML元素顺序来决定下一个控件,它就是我们在这种情况下所需要的。因此,tabindex表的分配将是这样的。

1   2   3   4
1   2   3   4
1   2   3   4
1   2   3   4

这是一次重大胜利。因为对于10X10表,我们不需要100个不同的选项卡索引。我们只用10个索引就可以做到。

这是一个用React演示这个想法的小例子。您可以运行它并查看。



// 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')
);
&#13;
<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>
&#13;
&#13;
&#13;

如果使用这种方法可以管理列数,我建议您使用第一个选项,为每个表分配不同的偏移量(也在上面的例子中说明)。如果列数是固定的,那么它将是简单。即使不是,您也可以尝试根据您的用例与固定的硬编码偏移量保持一定的合理差距。

如果这不起作用,那么您可以做的下一件事就是尝试从数据中计算偏移值。我假设您将拥有由此表表示的某种数据,如对象数组或数组数组,可能处于您的应用程序状态。因此,您可以根据这些数据导出表的偏移量。如果您使用像Redux这样的状态管理库,那将非常简单,我建议您查看reselect,它可以用来计算Redux中的派生状态。

希望这有帮助!

答案 1 :(得分:1)

对于常见的从左到右的DOM相关表示,它可以是:

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);
}

答案 2 :(得分:1)

以下是每次将新输入添加到布局时都可以调用的函数。它为每个输入分配tabIndex,没有任何间隙,并且容纳各种大小的表,其中每个单元可以具有任意数量的输入元素。您可以在 this jsfiddle 中对其进行测试。

输入元素存储在Map对象中,其中每个键是表,列和行索引的组合。然后对键进行排序,并按排序键的顺序设置tabIndex属性。

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);
}

<小时/> 注意:以下行导致IE出现问题,不支持the spread syntax

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

如果您必须支持IE,则可以致电map.forEach填充未排序的数组,如this modified jsfiddle所示。