37

I'd like to add tabindex to all form elements. The form is dynamic and I can't add it to the HTML. I would like to run it as a function.

If there are several radio buttons with the same name, each must have it's own tabindex value. Most of the form elements on page start as <input>, except <select>. How do I account for that?

I guess I will need to run a loop and add the attribute, right?

var n = 1; 

$('input, select').each(function() {               
    $(this).attr('tabindex', n++);
});
allenski
  • 1,652
  • 4
  • 23
  • 39
santa
  • 12,234
  • 49
  • 155
  • 255
  • hey @santa any chance you can update the accepted answer for this? I know this question may be a bit old, but still something folks may still be considering, however the only values that should be used are `0` or `-1`. See my answer below for recommended solution! – allenski Nov 23 '20 at 20:40

5 Answers5

55

Strange question, but yes that's the basic idea:

$(":input:not(:hidden)").each(function (i) { $(this).attr('tabindex', i + 1); });

This uses :input to get everything including buttons and text areas. :not(:hidden) will just exclude the hidden inputs to avoid unnecessary tabs.

Vijay
  • 2,965
  • 1
  • 15
  • 24
Brandon
  • 38,310
  • 8
  • 82
  • 87
  • 2
    Why is it strange? Some browsers have issues with tabbing through radiobuttons of the same group... Thanks. – santa Feb 27 '13 at 22:06
  • It is strange because sure you will get tab indexes but not in any guaranteed order. So you might find this causes you to tab through the radio buttons randomly. – Brandon Feb 27 '13 at 22:07
  • Won't it be going though my HTML structure? I don't have a fancy layout. – santa Feb 27 '13 at 22:09
  • 4
    Ah it looks like jQuery does indeed guarantee the order is in document order: http://stackoverflow.com/questions/1636201/is-the-order-objects-are-return-by-a-jquery-selector-specified – Brandon Feb 27 '13 at 22:16
  • 20
    $(":input:not(:hidden)").each(function (i) { $(this).attr('tabindex', i + 1); }); would even be better, because else the taborder will let you tab through hidden fields, too! – RSeidelsohn Apr 02 '14 at 10:22
  • Where would one put this call in an angularJS page? – jessewolfe Mar 01 '17 at 22:26
  • @jessewolfe sounds like an excellent idea for a new SO question. – Brandon Mar 01 '17 at 22:44
  • I would not recommend doing this way! See my answer below for a better approach. – allenski Dec 05 '18 at 17:53
17

Might be better to avoid n++ to set different tabindex numbers.

Instead, try setting tabindex to 0:

$(':input:visible').each(function() {               
    $(this).attr('tabindex', '0');
});

tabindex="0" means that the element should be focusable in sequential keyboard navigation, but its order is defined by the document's source order. ~ developer.mozilla.org

The :input selector basically selects all form controls.

The :visible selector basically selects all elements that are visible.


or as suggested in the comments, if you have no other changes to apply to each visible input, then this should be enough:

$(':input:visible').attr('tabindex', '0');

ES6 Update

Here's the ES6 version of the script using arrow function and template literals:

document.querySelectorAll(':input:visible').forEach(input => {
  input.setAttribute('tabindex', '0');
});

or we can use a single-line arrow function to set the tabindex attribute for each input element:

document.querySelectorAll(':input:visible').forEach(input => input.setAttribute('tabindex', '0'));
allenski
  • 1,652
  • 4
  • 23
  • 39
-1

Here, I described how can add aria-selected and tabindex value dynamically via jquery. I also want to see that how accessibility work with tablist, tab, and tabpanel role and how aria attributes work.Hope helps this code :

 var $tabs = $('.tabs');
 var $panels = $('.panel');

 $tabs.on('click', 'a', function (e) {
 e.preventDefault();
  var id = $(this).attr('href');

  // Find the currently visible tab and panel and hide them
$tabs.find('[aria-selected="true"]').attr({
            'aria-selected': false,
            'tabindex': -1
        });
   $(this).attr({
            'aria-selected': true,
            'tabindex': 0
        });
});

Tab Wrapper:-

<ul class="tabs" role="tablist">
  <li role="presentation"><a href="#tab-1" role="tab" aria-controls="tab-1" aria-selected="true">Tab 1</a></li>
  <li role="presentation"><a href="#tab-2" role="tab" aria-controls="tab-2">Tab 2</a></li>
  <li role="presentation"><a href="#tab-3" role="tab" aria-controls="tab-3">Tab 3</a></li>
</ul>

<div class="tab-panels">
  <div class="panel" id="tab-1" role="tabpanel" aria-hidden="false">…</div>
  <div class="panel" id="tab-2" role="tabpanel" aria-hidden="true">…</div>
  <div class="panel" id="tab-3" role="tabpanel" aria-hidden="true">…</div>
</div>
Devsaiful
  • 147
  • 4
-1

One approach is to move an element higher in the DOM. The element at the top of the DOM tree will be focused first using Tabs as compared to lower ones.

-1

For what's it's worth, I don't think you actually need to use an each loop here or even $(this).

jQuery is configured to execute functional arguments in place of plain values and runs the function once per loop, similarly to how an each loop would work.

You can read more on how that works in the official jQuery documentation here: .val( function )

So rather than using an each loop, you can accomplish the same effect using just this:

let n = 1;

$(':input:visible').attr('tabindex', function() { return n++; });

or if you're okay using the modern ES6 arrow function syntax—

let n = 1;

$(':input:visible').attr('tabindex', () => n++);

Using () => n++ instead of just n++ here allows jQuery to run the function for each instance's value rather than taking in the initial value of n++ and applying it to all matching elements.

Brandon McConnell
  • 5,776
  • 1
  • 20
  • 36