简体   繁体   English

反应动态tabindex创建

[英]React dynamic tabindex creation

I am searching for a way to dynamically create a tabindex for each element in my React application. 我正在寻找一种方法来动态创建我的React应用程序中每个元素的tabindex 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. 有多个组件包含我想要创建tabindex输入元素,我不知道如何协调它们并避免碰撞。

My React application is consisting of multiple tables containing input elements. 我的React应用程序由包含输入元素的多个表组成。 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. 我想改变用户通过这些输入进行选项卡的方向,但我不确定如何为每个元素生成tabindex

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) 每个表有一个固定的偏移量(但我不知道单个表中可能有多少输入, tabindex应该保持在32767以下,所以我不能假设巨大的差距)
  • 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) tabindex偏移传递给表并从React对象获取使用的tabindex数量以计算下一个偏移量(在我看来它会破坏模块性并且难以实现)
  • tracking the next tabindex through global state (hacky and would probably break when the table is extended) 通过全局状态跟踪下一个tabindex (hacky并且可能在表扩展时中断)
  • tracking the next tabindex through dom tree (no idea how to implement) 通过dom树跟踪下一个tabindex (不知道如何实现)

Is there a tool or a common practice for tabindex creation I am missing? 我缺少tabindex创建的工具或通用实践吗?

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. 您不需要为表中的每个单元格使用不同的tabindex来进行所需的选项卡导航。 You just need different tabindex for each column in your table. 您只需要为表中的每列提供不同的tabindex You can repeat the same tabindex for each cell in the same column. 您可以为同一列中的每个单元重复相同的tabindex 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. 因为当tabindex相同时,浏览器只使用HTML元素顺序来决定下一个控件,这就是我们在这种情况下所需要的。 So tabindex assignment for a table will be something like this. 因此,表格的tabindex分配将是这样的。

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. 因为对于10X10表,我们不需要100个不同的选项卡索引。 We can do it with just 10 indexes. 我们只需10个索引即可完成。

Here is a small example to demonstrate this idea with React. 这是一个用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. 如果您正在使用像Redux这样的状态管理库,那么它将非常简单,我建议您查看可用于在Redux中计算派生状态的重新选择

Hope this helps! 希望这可以帮助!

For a common left-to-right-down DOM related representation it could something like: 对于常见的从左到右的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);
}

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 accommodates tables of various sizes, where each cell can have any number of input elements. 它为每个输入分配tabIndex ,没有任何间隙,并且容纳各种大小的表,其中每个单元可以具有任意数量的输入元素。 You can test it in this jsfiddle . 你可以在这个jsfiddle中测试它。

The input elements are stored in a Map object, where each key is a combination of table, column, and row indices. 输入元素存储在Map对象中,其中每个键是表,列和行索引的组合。 The keys are then sorted, and the tabIndex property is set in the order of the sorted keys. 然后对键进行排序,并按排序键的顺序设置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);
}


Note: the following line causes trouble in IE, which does not support the spread syntax : 注意:以下行导致IE中出现问题,它不支持扩展语法

 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 . 如果必须支持IE,则可以调用map.forEach来填充未排序的数组, 如此修改后的jsfiddle所示。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM