11

How can I use jQuery to set up the tabbing order of a table with input elements so that the tab-order will be vertical (down each of the columns) instead of the default horizontal method?

The numbers below represent the tabbing order I would like. I'd the jQuery code to work independently of the number of rows and columns in the table.

Example Table (rendered as an image unfortunately)

Picture 1.png http://img263.imageshack.us/img263/9125/picture1pjf.png

Example table HTML Code:

<table>
 <tr>
  <td><input type="text" /></td> <!-- 1 -->
  <td><input type="text" /></td> <!-- 4 -->
  <td><input type="text" /></td> <!-- 7 --> 
 </tr>
 <tr>
  <td><input type="text" /></td> <!-- 2 -->
  <td><input type="text" /></td> <!-- 5 -->
  <td><input type="text" /></td> <!-- 8 -->
 </tr>
 <tr>
  <td><input type="text" /></td> <!-- 3 -->
  <td><input type="text" /></td> <!-- 6 -->
  <td><input type="text" /></td> <!-- 9 -->
 </tr>
</table>
cwd
  • 53,018
  • 53
  • 161
  • 198
  • The second cell in the last row should read 6 :) – BoltClock Dec 27 '11 at 04:28
  • You want it to be using jquery or just by using HTML? – Virendra Dec 27 '11 at 04:31
  • @Virendra - I need to dynamically change the table using jQuery after it has loaded without any modified HTML. – cwd Dec 27 '11 at 04:35
  • @BoltClock - updated it, thanks. Also good call with your (deleted) answer - it would have been a clever way to do it if the numbers were available in the HTML code ;) – cwd Dec 27 '11 at 04:35
  • How are you generating the table? Is it dynamic or static table? Also, I am assuming the number of rows and columns can change. – Virendra Dec 27 '11 at 04:38
  • @cwd: No problem, I wasn't being serious with my answer anyway :) – BoltClock Dec 27 '11 at 04:48

2 Answers2

22

Here's one way to do it:

$(function() {

    $('tr').each(function() {
        // For each row

        $(this).find('td').each(function(i) {
            // For each cell in that row (using i as a counter)

            $(this).find('input').attr('tabindex', i+1);
            // Set the tabindex of each input in that cell to the counter

        });
        // Counter gets reset for every row
    });
});

What this achieves is something like this:

1 2 3 4 5

1 2 3 4 5

1 2 3 4 5

So that by tabbing, you first go through all the ones, then all the twos, and so on.

Example: http://jsfiddle.net/grc4/G5F7S/3/

EDIT:

To avoid the problem when there are other input fields, you can simply add an offset to each tabindex. This won't always get the order perfect, but it's better than nothing.

The updated example is here: http://jsfiddle.net/grc4/G5F7S/6/

grc
  • 22,885
  • 5
  • 42
  • 63
  • @Purmou: Simple: Each `.find('td')` for a row returns three cells, indices 0, 1, 2. The tabindexes assigned are 1, 2, 3 in the same order. Every row of inputs have the same set of tabindexes. All the `tabindex="1"` inputs are tabbed first, from top to bottom, then the `tabindex="2"` inputs, then the `tabindex="3"` inputs. The same tabindex can be assigned to multiple elements without any problems, and this behavior is documented in the HTML spec. – BoltClock Dec 27 '11 at 04:45
  • Probably this would work. So here you are assigning tab index of 1,2,3 to rows in the same column. – Virendra Dec 27 '11 at 04:48
  • This will work in case there are no other input fields. However, if you have an input field before the table with say `tabindex="3"` it would not work correctly. Here is a fiddle to explain what I mean. http://jsfiddle.net/G5F7S/5/ – Virendra Dec 27 '11 at 04:50
  • 2
    +1; Good way to (ab)use the HTML spec to do your bidding :) Nice and simple code. – Merlyn Morgan-Graham Dec 27 '11 at 04:51
  • 1
    Tricky but a simple and clever solution. +1 – AkiEru Nov 10 '15 at 06:48
  • @grc very elegant and simple solution. How to prevent counter reset each row and instead, have a sequential numbering like, 1, 2, 3, 4, 5, 6, 7, 8, 9 and so on. or like what OP has in his example – user5249203 Jul 22 '16 at 15:23
2

The logic is a bit easier if you have rectangular tables, but here's a solution that should handle any case:

// Count the max number of columns in all rows of the table
var maxRowCount = 0;

// Set columnCount to the count of the row with the most cells
// Loop through all rows in case some rows are larger than others
$('table tr').each(function() {
  maxRowCount = Math.max(maxRowCount, $(this).children('td').length);
});

// Number all of the cells in the table
var cellCounter = 1;

// Loop through the table, column first instead of row first
for (var columnIndex = 0; columnIndex < maxRowCount; ++columnIndex) {
  // Loop through all the rows for the current column
  $('table tr').each(function() {
    // ...but only look at cells matching the current column
    var cellForCurrentColumn = $(this)
    .children('td')
    .eq(columnIndex)
    .find('input');

    // Some rows could be bigger than others,
    // so cellForCurrentColumn might not exist for shorter rows
    if (cellForCurrentColumn != null) {
      // Set the tab index and then increment the counter
      cellForCurrentColumn.attr('tabindex', cellCounter++);
    }
  });
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
  <tr>
      <th></th>
      <th>column 1</th>
      <th>column 2</th>
      <th>column 3</th>
      <th>column 4</th>
  </tr>
  <tr>
    <th>row 1</th>
    <td>1<br/><input type="text"/></td>
    <td>5<br/><input type="text"/></td>
  </tr>
  <tr>
    <th>row 2</th>
    <td>2 (non-text input)<br/><input type="button" value="Push me"/></td>
    <td>6<br/><input type="text"/></td>
    <td>9<br/><input type="text"/></td>
    <td>12<br/><input type="text"/></td>
  </tr>
  <tr>
    <th>row 3</th>
    <td>3<br/><input type="text"/></td>
    <td>7 (multiple inputs)<br/><input type="text"/><input type="text"/></td>
    <td>10 (inline comment)<br/><input type="text"/><!-- inline comment --></td>
  </tr>
  <tr>
    <th>row 4</th>
    <td>4<br/><input type="text"/></td>
    <td>8 (disabled input)<br/><input type="text" placeholder="disabled" disabled/></td>
    <td>11<br/><input type="text"/></td>
    <td>13<br/><input type="text"/></td>
  </tr>
</table>
Merlyn Morgan-Graham
  • 58,163
  • 16
  • 128
  • 183
  • 1
    This doesn't actually do what the OP wants. – Purag Dec 27 '11 at 04:52
  • As the OP I have to agree with @Purmou – cwd Dec 27 '11 at 04:56
  • @Purmou, cwd: Was starting tab index from 0 (doesn't work), and was applying to the `td` instead of the `input`. Found the bug, fixed, and updated the post. Thanks :) – Merlyn Morgan-Graham Dec 27 '11 at 04:56
  • how could you modify this, so it circulate a table vertically, and then jump to another table and do the same thing. for example, i have a table ( A ) with 2 rows and 2 cells, and each of those cells have tables ( B, C, D, E ) with form elements. now cursor is in a textbox of the table B, pressing tab it should circulate all other textboxes vertically ( as exactly you did above ), and when finished, should jump to first control in table C, and so on ! – armen Jun 06 '12 at 07:54
  • @armen: Sounds like it might deserve its own question on here because the requirements are somewhat different. If you want to write one up I'd be happy to take a look. Include some example HTML too if you can. – Merlyn Morgan-Graham Jun 06 '12 at 08:37
  • @MerlynMorgan-Graham, Would you mind to comment the steps to help understand better. Thank you – user5249203 Jul 22 '16 at 15:35
  • @MerlynMorgan-Graham, how do we specify numbering only the input type text ( avoid disabled, headings, links, text area etc.) – user5249203 Jul 22 '16 at 17:45
  • @user5249203 I've annotated the existing code. Let me know if it is still confusing. – Merlyn Morgan-Graham Jul 22 '16 at 18:31
  • @user5249203 "how do we specify numbering only the input type text" - my code already does this by using a jQuery selector for `input`. "avoid disabled, headings, links, text area etc" - my code doesn't avoid disabled cells, but does avoid things that aren't inside a `td`, that aren't an `input` element. Figuring out how to create the correct jQuery selectors to do that sort of thing is verging into "not the same question" territory, as the original question didn't have these requirements. You are free to make another separate question, since they are independent problems. – Merlyn Morgan-Graham Jul 22 '16 at 18:33
  • @MerlynMorgan-Graham, thank you for your inputs. I am trying to use your solution and combine with a Q I asked here http://stackoverflow.com/questions/38489379/enter-key-to-follow-the-tabindex-in-scenario-where-enter-key-is-changed-to-beha/38489926?noredirect=1#comment64455674_38489926. It works fine, but I noticed that once the cursor reaches last cell of the first column it does not move to the new column by default. – user5249203 Jul 22 '16 at 18:37
  • @user5249203 I could augment this to deal with disabled cells, though, as well as multiple input elements in each cell. I will go ahead and do that. Thanks for pointing it out. It inherently will deal with "not an input element" and "not a cell" though. – Merlyn Morgan-Graham Jul 22 '16 at 18:37
  • @user5249203 Well, looks like it already deals with some cases I didn't think it would deal with. Look through the new HTML code and let me know if there are some cases that aren't yet handled. – Merlyn Morgan-Graham Jul 22 '16 at 18:57
  • @MerlynMorgan-Graham, I may be reviving a 4 year old post here. i just noted that , this does not handle ``. how to specify not case) – user5249203 Jul 22 '16 at 20:56
  • @user5249203 jQuery selectors pick what you tell them to pick. They usually ignore what you don't explicitly tell them to pick. My jQuery selectors filter for specific elements, and ignore those that aren't what they're looking for. – Merlyn Morgan-Graham Jul 22 '16 at 21:09
  • @user5249203 I am not very good with HTML tables (I'm a back-end developer primarily), so I can't tell exactly what you mean. If you can put up an HTML example on jsfiddle or pastebin or github gist (google for those sites) then I can look in more detail and let you know if it is a problem. I also strongly encourage you to play with live examples and see if you're actually finding a problem, rather than only examining the code and guessing at what problems might arise. Do both these things :) – Merlyn Morgan-Graham Jul 22 '16 at 21:10
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/118065/discussion-between-merlyn-morgan-graham-and-user5249203). – Merlyn Morgan-Graham Jul 22 '16 at 21:16