2017-05-22 2 views
4

Je cherche un moyen de créer dynamiquement un tabindex pour chaque élément de mon application React. Il y a plusieurs composants contenant des éléments d'entrée que je veux créer tabindex et je ne sais pas comment les coordonner et éviter les collisions.Réagir création de tabindex dynamique

Mon application React consiste en plusieurs tables contenant des éléments d'entrée. Lors de l'exécution, les tables peuvent être étendues par l'utilisateur en créant des éléments d'entrée supplémentaires. Je veux changer la direction dans laquelle l'utilisateur tabule à travers ces entrées, mais je ne suis pas sûr comment je peux générer un tabindex pour chaque élément.

Les façons je suis venu avec à ce jour:

  • ont un décalage fixe pour chaque table (mais je ne sais pas combien d'entrées peuvent être dans une seule table et tabindex devraient rester en dessous de 32767 si je ne peux pas simplement supposer d'énormes lacunes)
  • passe tabindex offset sur la table et obtenir le montant de utilisée tabindex de React objet pour le calcul suivant décalage (me semble que ce serait briser la modularité et difficile à mettre en œuvre)
  • suivi le prochain tabindex par mondial état (hacky et serait probablement b Reak lorsque la table est prolongée)
  • suivi le prochain tabindex par arbre dom (aucune idée de la façon de mettre en œuvre)

est-il un outil ou une pratique commune pour la création tabindex je suis absent?

+1

Je pense que ce problème valide doit être adressé diferrently - certains [idées intéressantes dans les composants Web] (https://groups.google.com/forum/m/#!topic/polymer-dev/1F2OrtU8ezU) –

+0

Généralement, la meilleure pratique consiste à éviter d'utiliser des entiers positifs pour 'tabindex'. Pouvez-vous donner un contexte sur ce que vous entendez par «modifier la direction dans laquelle l'utilisateur tabule ces entrées». Cela nous aide probablement à vous donner une meilleure réponse. –

+0

J'ai plusieurs tables et j'aimerais que les tabulations les traversent d'abord d'abord (de haut en bas), puis de gauche à droite jusqu'à la colonne suivante après que la dernière rangée ait été atteinte. – thammi

Répondre

3

Je ne sais pas si vous l'avez déjà compris ou non. Mais il y a un truc que vous pouvez utiliser ici.

Vous n'avez pas besoin d'avoir un tabindex différent pour chaque cellule de la table pour effectuer la navigation par tabulation souhaitée. Vous avez juste besoin de tabindex différent pour chaque colonne de votre tableau. Vous pouvez répéter le même tabindex pour chaque cellule de la même colonne. Parce que quand tabindex est le même, le navigateur utilise simplement l'ordre des éléments HTML pour décider du contrôle suivant et c'est ce dont nous avons besoin dans ce cas.Donc tabindex affectation pour une table sera quelque chose comme ça.

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

Ceci est une victoire significative. Parce que pour une table 10X10, nous n'avons pas besoin de 100 index de tabulation différents. Nous pouvons le faire avec seulement 10 index.

Voici un petit exemple pour illustrer cette idée avec React. Vous pouvez l'exécuter et voir.

// 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>

Si le nombre de colonnes est gérable avec cette approche, je vous recommande d'aller avec votre première option en attribuant des paramètres différents pour chaque table (également illustré dans l'exemple ci-dessus) .Si le nombre de colonnes est fixe alors ce sera facile. Même si ce n'est pas le cas, vous pouvez essayer de conserver des écarts raisonnables avec des décalages codés en dur basés sur votre cas d'utilisation.

Si cela ne fonctionne pas de cette façon, la prochaine chose que vous pouvez faire est d'essayer de calculer des valeurs de décalage à partir de vos données. Je suppose que vous aurez une sorte de données qui sont représentées par ces tables, comme un tableau d'objets ou un tableau de tableaux, probablement dans votre état d'application. Vous pouvez donc calculer des offsets pour des tables basées sur ces données. Si vous utilisez une bibliothèque de gestion d'état comme Redux, ce sera vraiment facile et je recommande de regarder reselect qui peut utiliser pour calculer l'état dérivé dans Redux.

Espérons que cela aide!

+0

Merci, c'était l'indice parfait pour mon application. D'autant plus que ça me permet de résoudre ça sans contourner React. – thammi

+0

@thammi Je suis content que cela vous a aidé. Mais je pense que vous avez oublié de marquer cela comme la bonne réponse. :) –

+0

Oh ouais. Trop de boutons pour appuyer avec une prime. – thammi

1

Pour DOM commun gauche à droite vers le bas la représentation liée il pourrait quelque chose comme:

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

Voici une fonction que vous pouvez appeler à chaque fois que de nouvelles entrées sont ajoutées à votre mise en page. Il attribue un tabIndex à chaque entrée, sans aucun espace, et accepte des tables de différentes tailles, où chaque cellule peut avoir un nombre quelconque d'éléments d'entrée. Vous pouvez le tester en this jsfiddle.

Les éléments d'entrée sont stockés dans un objet Map, où chaque clé est une combinaison d'index de table, de colonne et de ligne. Les clés sont ensuite triées et la propriété tabIndex est définie dans l'ordre des clés triées.

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: la ligne suivante provoque des problèmes dans IE, qui ne supporte pas the spread syntax:

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

Si vous devez prendre en charge IE, vous pouvez appeler map.forEach pour remplir le tableau non trié, comme le montre au this modified jsfiddle.