Search code examples
javascriptinternet-explorerasynchronousonchangetabindex

Weird Behaviour When Changing the tabindex in onchange Handler in Internet Explorer


Given the following code:

<html>
<head>
<script>
  function updateTabIndex() {
    setTimeout(function() {
      var elem = document.getElementById("id1")
      elem.setAttribute("tabIndex", 2)
      elem.setAttribute("value", "now: tabindex 2")
    }, 100)
  }
</script>
</head>
<body>
  <h1>Weird Behaviour When Changing the tabindex in onchange Handler in Internet
    Explorer</h1>
  <p><input id="id1" tabindex="-1" value="tabindex: (-1)"/></p>
  <p><select tabindex="1" size="1" onchange="updateTabIndex()" onpropertychange="updateTabIndex()">
    <option>tabindex 1 - an option</option>
    <option>tabindex 1 - another option</option>
  </select></p>
  <p><input tabIndex="3" value="tabindex 3"/></p>


  <h2>Instructions</h2>
  <ol>
    <li>Open this page in IE. To be precise, the problem is reproducible in 
    (at least):
    <ul>
      <li>IE 8, Version 8.0.6001.18702CO on Windows XP and</li>
      <li>IE 9, Version 9.0.8112.16421 on Windows 7 Professional, 64 bit</li>
    </ul>
    <li>Use the <em>mouse</em> to change the value of the select box.</li>
    <li>As you can see, the onchange handler is called and changes the tabindex
    of the first input element.</li>
    <li>If you now tab out of the select box, you can see that the tab order is
    adhered to correctly: you go from the select box to the first input element
    and then to the last element on the page.</li>
    <li>Close the page and open it again.</li>
    <li>Click on the third input element and press Shift + Tab to enter the select
    box.</li>
    <li>Use the down arrow key on the keyboard to change the selected value in the
    select box.</li>
    <li>Now tab out of the select box.</li>
    <li>As you can see, the onchange handler is called, as before (the text in 
    the first input field has changed).</li>
    <li>However, if you tab around now, you will see that the tabindex attribute
    is ignored. Instead the order of the elements in the document source is used
    as the tab order.</li>
    <li>Even if you change the select box value with the mouse now, the tab
    order remains broken. To test again, you need to close the page and open
    it again or reload it by entering the url bar and pressing enter. Hitting
    F5 will not be enough.</li>
 </ol>
 </body>
</html>

IE ignores the changed tab order when the select box value (which triggers the change to the tabindex attribute) is changed by some keyboard action (arrow keys) but does not ignore the tabindex change when the select box value is changed with the mouse (see more detailed explanation about the expected and perceived behaviour in html above).

Why does IE behaves like that? In particular, why is the behaviour different when using the mouse and when using the keys. Is it a bug in IE? Right now, I'm pretty sure it is a bug in IE here, but of course there is also the chance that I'm doing something wrong which just happens to work as expected in FF.

I already googled quite a bit, but as far as I can see, this bug (if it is one) has not been reported or discussed anywhere until now.

Some remarks

First of all, Firefox does not exhibit this weird behaviour.

Just to avoid being misunderstood, it is clear that, when I tab out from the select box, I'll get to the to the input with tabindex 3, because the asynchronous code in the onchange handler might not have been executed yet. But after that (when the text in the first input has changed), I expect the new tab order to be established. This is how FF behaves.

It seems that IE already fires the onchange event each time I use the up/down arrow key and not when I leave the select box (as FF does). I'm not sure if that is related to the problem.

Added Bonus: If you open the IE developer tools you will see that the tabIndex attribute is set correctly in the DOM, also when changing the select box value without the mouse. But the tabindex is ignored when tabbing around.

My (naive) guess is that IE keeps an internal representation of the taborder and that this internal representation is not recalculated correctly in this case.

More weirdness: You can delete lines 5 and 9 of this file (setTimeout call and closing curly brace), so that the tabindex change is no longer called asynchronously. It then works correctly if you enter the select with the mouse and leave it by tabbing out. It still breaks if you tab into the select change the value with the arrow key and tab out.


Solution

  • A colleague of mine found an interesting workaround: Just add

    elem.parentNode.replaceChild(elem, elem)
    

    after setting the tabindex makes it work. Seems this DOM manipulation triggers a recalculation of IE's internal tab order which is missing otherwise.

    While this is better than no solution, it's quite an ugly hack. :-(

    If anybody knows a different, less brute force solution, I'd be still interested.