Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"The tabIndex IDL attribute must reflect the val..." #1786

Closed
cdumez opened this issue Sep 15, 2016 · 15 comments
Closed

"The tabIndex IDL attribute must reflect the val..." #1786

cdumez opened this issue Sep 15, 2016 · 15 comments

Comments

@cdumez
Copy link

cdumez commented Sep 15, 2016

https://html.spec.whatwg.org/#the-tabindex-attribute

The tabIndex IDL attribute must reflect the value of the tabindex content attribute. Its default value is 0 for elements that are focusable and −1 for elements that are not focusable.

It seems Firefox is the only browser that behaves this way currently.

Chrome / Safari always return -1 for HTML elements that are not focusable (even after setting tabIndex to something else).

Edge seems to allow setting the tabIndex of non-focusable elements. However, it initially returns 0 instead of -1 which seems to hint that it does not treat non-focusable elements differently when it comes to tabIndex.

I personally do not mind updating Safari to match Firefox if people prefer the behavior that is currently specified. However, it may be easier for Firefox to align with Safari / Chrome.

@cdumez
Copy link
Author

cdumez commented Sep 15, 2016

@domenic @bzbarsky @foolip

@domenic
Copy link
Member

domenic commented Sep 15, 2016

The situation is a bit more messy than that, I think. See https://www.w3.org/Bugs/Public/show_bug.cgi?id=29093 and an attempt to fix it (not really very successful) in #938.

@cdumez
Copy link
Author

cdumez commented Sep 15, 2016

It is true that WebKit (and likely Blink) will currently not return -1 for form control elements. This part, I definitely do not mind fixing.

The part I would likely clarification on is:
"value is initially 0 (or -1 if not focusable)"
vs
"value is always -1 if not focusable. Otherwise, it reflects the corresponding attribute and is initially 0".

This seems to be something where Firefox and WebKit / Chrome disagree.

@bzbarsky
Copy link
Contributor

bzbarsky commented Sep 15, 2016

As I pointed out in #938, "focusable" is not really a boolean state. There are things that can be focused with the mouse, things that can be focused with the keyboard, things that can be focused with both, things that can be focused with neither. Oh, and tabbing is somewhat indepedent of focusability, in that some things may be focusable with keyboard (e.g. via accesskeys) but not in the tab order....

All that said, I'd like to better understand this situation in which the tabindex attribute is set but the element is not focusable. Looks like per https://html.spec.whatwg.org/multipage/interaction.html#data-model:specially-focusable this can happen for disabled form controls, elements that are display:none (or have ancestors that are), elements that are used as canvas fallback content, and the inert stuff, right?

I would really rather not have the value of tabIndex depend on CSS display. And in Safari it doesn't, so whatever WebKit is doing it's not actually looking at whether elements are focusable or not, but rather at something else.

@bzbarsky
Copy link
Contributor

And looks like for a disabled input both Safari and WebKit reflect the content attribute, instead of returning -1, even though it's very much not focusable. Same for Chrome.

@cdumez, when you say "HTML elements that are not focusable", what do you mean, exactly?

@bzbarsky
Copy link
Contributor

Testcase, just for posterity:

<style>
  input { display: none; }
</style>
<input tabindex="-2">
<input tabindex="-1">
<input tabindex="0">
<input tabindex="1">
<input tabindex="2">
<input tabindex="3" disabled>
<input tabindex="4">
<script>
  var list = document.getElementsByTagName("input");
  for (var idx = 0; idx < list.length; ++idx) {
    console.log(list[idx].tabIndex);
  }
</script>

@cdumez
Copy link
Author

cdumez commented Sep 15, 2016

And in Safari it doesn't, so whatever definition WebKit is doing it's not actually looking at whether elements are focusable or not, but rather at something else.

Looking at the code, elements that are not rendered are not considered as focusable in the context of tabIndex. Therefore, we would return -1.

The reason you test shows differently is because you are testing with a form control element (input) and WebKit bypasses the isFocusable() check for form control elements for some reason.

@bzbarsky
Copy link
Contributor

OK, how about this testcase:

<style>
  span { display: none; }
</style>
<span tabindex="-2">Hey</span>
<span tabindex="-1">Hey</span>
<span tabindex="0">Hey</span>
<span tabindex="1">Hey</span>
<span tabindex="2">Hey</span>
<span tabindex="3">Hey</span>
<span tabindex="4">Hey</span>
<script>
  var list = document.getElementsByTagName("span");
  for (var idx = 0; idx < list.length; ++idx) {
    console.log(list[idx].tabIndex);
  }
</script>

WebKit nightly is reflecting the attribute there too...

@bzbarsky
Copy link
Contributor

bzbarsky commented Sep 15, 2016

Or this:

<script>
  var s = document.createElement("span");
  s.setAttribute("tabindex", "5");
  alert(s.tabIndex);
</script>

alerts 5 in WebKit afaict.

@cdumez
Copy link
Author

cdumez commented Sep 15, 2016

Ok, you're right. In WebKit, it looks like our tabIndex() does:
return supportsFocus() ? raredata->tabIndex : -1;

However, our definition of supportsFocus() is quite complex:

  • For form control elements, it seems it returns true unless the control is disabled
  • We have some more custom behavior for anchor, body, area, frames, media elements, summary, plugins, ..
  • Otherwise, we fallback to returning true if the tabIndex was set explicitly.

Unlike what I said earlier, we don't check style in the context of tabIndex.

@bzbarsky
Copy link
Contributor

OK, given that, what would you propose the spec says? Do you think it should try to duplicate WebKit's definition of "supportsFocus"? Do you think it should define some predicate like that which will evaluate differently in different browsers depending on what they wan to do? Something else?

My personal preference, honestly, is that if the tabindex attribute is set, we should reflect it as-is. If not set, things are complicated. :(

@cdumez
Copy link
Author

cdumez commented Sep 16, 2016

@rniwa do you have any thoughts on this?

I personally would not mind reflecting as-is, without isFocusable() checks. We can have whatever logic internally to determine the -actual- tabIndex but for the IDL attribute, a simple reflection seems fine.

Also, I did not do extensive testing of Edge but it looked to me that it does simple reflection as well.

@rakina
Copy link
Member

rakina commented Jun 24, 2019

As the tests in #4464 (comment) and #4607 (comment) show, the behavior is pretty consistent across vendors (except for the case with a custom element with delegatesFocus)

We should probably update https://html.spec.whatwg.org/multipage/interaction.html#dom-tabindex to make the getter return the attribute value if set, or do a simple element type check otherwise (returns 0 if it's one of <input>, <button>, <a>, <textarea>, <select>, <iframe>, <area>, -1 otherwise).

To solve #4464 (comment), we might need to expose new functions along with new concepts/definitions for "sequentially focusable" and/or "programmatically focusable" mentioned in #4607 (comment), but that should be separate from the tabIndex getter.

@rniwa
Copy link

rniwa commented Jun 25, 2019

I think reflecting the tabindex content attribute the easiest path forward for interoperability.

The state of whether an element is focus or not is, as @bzbarsky has already noted, a very much non-binary and complex situation so I don't think spec'ing what WebKit and Chrome do right now makes much sense although I'm afraid there might be some Web content out there which depends on WebKit/Chrome's behavior already :(

@domenic
Copy link
Member

domenic commented Jun 26, 2019

To be clear, reflecting is the plan, but the question is what is the default value when the attribute is absent. @rakina's proposal (which I agree with) is to just match all existing browsers and return 0 for a certain list of elements, or -1 otherwise.

Since this generally matches browsers, with only a few exceptions (we can probably find the exceptions in detail when we write tests), I expect it'll be compatible with existing web content, and also the easiest path toward interop.

I agree we shouldn't have the default value depend on some binary notion of focusability like the current spec says.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests

5 participants