Intent to Implement: isTabStop property on Element

102 views
Skip to first unread message

Takayoshi Kochi (河内 隆仁)

unread,
Feb 24, 2015, 2:14:36 AM2/24/15
to blink-dev
Contact emails

Design doc:
(The doc is also public-webapps@w3c for open discussion)

Summary
Historically "tabIndex" property meant that an element can be keyboard focusable
as well as defining the order of the keyboard focus.  IsTabStop intends to expose
the former (both readable/writable) to decouple the functionality from tabindex (though
we try to keep "tabindex" as is as much as possible) and giving more
flexible control of focusability of a DOM element.

Motivation
When a web component designer wants to control the focusability and ordering of
keyboard navigation at the same time using tabindex, especially when shadow DOM is
involved, the keyboard navigation gets messy, as the outline of the component joins
in the keyboard navigation list ring. This also tries to explain HTML's "tabindex focus flag"
so that custom elements can emulate natively focusable elements (e.g. <a>, <input>).

Compatibility Risk
Low.
This is a new property under "experimental web platform features" flag.

There are still on-going discussion about the spec (see the design doc for details).
Our plan is to merge this in the shadow DOM spec (and eventually the HTML spec).

Ongoing technical constraints

None.

Will this feature be supported on all six Blink platforms (Windows, Mac, Linux, Chrome OS, Android, and Android WebView)?
Yes

OWP launch tracking bug

Requesting approval to ship?
No.

--
Takayoshi Kochi

Elliott Sprehn

unread,
Feb 28, 2015, 1:17:58 AM2/28/15
to Takayoshi Kochi (河内 隆仁), blink-dev
I'd like to suggest we call this tabStop instead so the attribute can also be tabstop.

<some-widget tabstop="false" tabindex="10">

Anne van Kesteren

unread,
Feb 28, 2015, 3:31:41 AM2/28/15
to Elliott Sprehn, Takayoshi Kochi (河内 隆仁), blink-dev
On Sat, Feb 28, 2015 at 7:17 AM, Elliott Sprehn <esp...@chromium.org> wrote:
> I'd like to suggest we call this tabStop instead so the attribute can also
> be tabstop.
>
> <some-widget tabstop="false" tabindex="10">

Then it should be tabstop=tabstop per
https://html.spec.whatwg.org/multipage/infrastructure.html#boolean-attributes


--
https://annevankesteren.nl/

Elliott Sprehn

unread,
Feb 28, 2015, 5:05:56 AM2/28/15
to Anne van Kesteren, Takayoshi Kochi (河内 隆仁), blink-dev
How do you set it to false declaratively on a non reflected attribute?
 


--
https://annevankesteren.nl/

Anne van Kesteren

unread,
Feb 28, 2015, 6:28:58 AM2/28/15
to Elliott Sprehn, Takayoshi Kochi (河内 隆仁), blink-dev
Remove the attribute. See the reflect definition.

--
https://annevankesteren.nl/

Elliott Sprehn

unread,
Feb 28, 2015, 11:16:42 AM2/28/15
to Anne van Kesteren, Takayoshi Kochi (河内 隆仁), blink-dev


On Saturday, February 28, 2015, Anne van Kesteren <ann...@annevk.nl> wrote:
Remove the attribute. See the reflect definition.

There's no attribute to remove.

<a> is a tab stop by default, I need some way to turn that off declaratively. So I need <a tabstop=false> in markup.

<div> is not a tab stop, I need some way to turn that on declaratively. So I need <div tabstop=true> in markup.

To use boolean attributes you'd need both tabstop and notabstop attributes.

- E

Philip Jägenstedt

unread,
Mar 1, 2015, 10:05:56 PM3/1/15
to Elliott Sprehn, Anne van Kesteren, Takayoshi Kochi (河内 隆仁), blink-dev
It sounds like a boolean attribute isn't a good fit. Maybe an enumerated attribute instead, like the translate (yes/no), autocomplete (on/off) or spellcheck (true/false) attribute?

Philip

Takayoshi Kochi (河内 隆仁)

unread,
Mar 4, 2015, 4:59:17 AM3/4/15
to Philip Jägenstedt, Elliott Sprehn, Anne van Kesteren, blink-dev
Thanks for the feedback.

As Phillip pointed out, enumerated attribute sounds good fit to me.
Also, the name tabstop(attribute)/tabStop(property) sounds more consistent to
others.

I should clarify in the doc that when both tabindex and tabstop attribute are specified
on an element, tabStop has priority.
e.g. <div tabindex=0 tabstop=false> = <div tabstop=false tabindex=0>

(tabindex=0 implies tabstop=true, but by having explicit tabstop attribute, tabstop
will always be honored, independent of the appearance order)

<div id="foo" tabstop=false>
$('foo').tabIndex = 0

should also be the same.

BTW, do we have any other similar case (order of appearance of attributes may differentiate
the meaning) in HTML or does the HTML spec explicitly say the order of attributes
should not be relevant?

--
Takayoshi Kochi

Anne van Kesteren

unread,
Mar 4, 2015, 5:04:10 AM3/4/15
to Takayoshi Kochi (河内 隆仁), Philip Jägenstedt, Elliott Sprehn, blink-dev
On Wed, Mar 4, 2015 at 10:58 AM, Takayoshi Kochi (河内 隆仁)
<ko...@google.com> wrote:
> BTW, do we have any other similar case (order of appearance of attributes
> may differentiate
> the meaning) in HTML or does the HTML spec explicitly say the order of
> attributes
> should not be relevant?

Order should not be relevant when it comes to semantics. (It is
exposed in certain cases.)


--
https://annevankesteren.nl/

Daniel Freedman

unread,
Mar 9, 2015, 8:36:43 PM3/9/15
to Anne van Kesteren, Takayoshi Kochi (河内 隆仁), Philip Jägenstedt, Elliott Sprehn, blink-dev
Just to confirm, would <div tabstop=true> be equivalent in focus behavior to <div tabindex=0>?

Takayoshi Kochi (河内 隆仁)

unread,
Mar 10, 2015, 6:10:08 AM3/10/15
to Daniel Freedman, Anne van Kesteren, Philip Jägenstedt, Elliott Sprehn, blink-dev

On Tue, Mar 10, 2015 at 9:36 AM, Daniel Freedman <dfr...@chromium.org> wrote:
Just to confirm, would <div tabstop=true> be equivalent in focus behavior to <div tabindex=0>?

Yes.



--
Takayoshi Kochi

Simon Pieters

unread,
Mar 10, 2015, 12:35:15 PM3/10/15
to Daniel Freedman, 'Takayoshi Kochi (河内 隆仁)' via blink-dev, Takayoshi Kochi (河内 隆仁), Anne van Kesteren, Philip Jägenstedt, Elliott Sprehn
Can you explain again why tabindex=0 and tabindex=-1 are not enough? I've
read through the discussion but I don't get it. It seems to me the new
attribute doesn't give any new abilities over tabindex. It also doesn't
"explain" e.g. <a href> since whether <a href> is a tabstop or not depends
on platform conventions per spec...

cheers
--
Simon Pieters
Opera Software

Domenic Denicola

unread,
Mar 11, 2015, 12:38:21 AM3/11/15
to Simon Pieters, Daniel Freedman, 'Takayoshi Kochi (河内 隆仁)' via blink-d, Takayoshi Kochi (河内 隆仁), Anne van Kesteren, Philip Jägenstedt, Elliott Sprehn
I think something got lost in this discussion.

isTabStop should not be reflected into an attribute. It is meant to represent the *default* tab-stoppiness of an element. The author-facing interface for changing tab-stoppiness is, as always, the tabindex attribute. In other words, isTabStop should control the "tab index focus flag" from HTML, which is not reflected as an attribute.

Here is a more concrete model:

- Every element two internal slots: [[TabIndexFocusFlag]] and [[TabIndex]].
- isTabStop getter/setter returns/sets the [[TabIndexFocusFlag]] slot.
- tabIndex getter/setter and tabindex="" attribute set the [[TabIndex]] slot.
- Per the rules in https://html.spec.whatwg.org/multipage/interaction.html#the-tabindex-attribute the UA figures out if an element is focusable by consulting both [[TabIndexFocusFlag]] and [[TabIndex]] in some combination.

So for example:

- <a> has [[TabIndexFocusFlag]] = false, [[TabIndex]] = "", and so it is not focusable.
- <a href="http://example.com/"> has [[TabIndexFocusFlag]] = true, [[TabIndex]] = "", and so it is focusable.
- <a tabindex="0"> has [[TabIndexFocusFlag]] = false, [[TabIndex]] = "0", and so it is focusable.
- <a href="http://example.com/" tabindex="-1"> has [[TabIndexFocusFlag]] = true, [[TabIndex]] = "-1", and so it is not focusable.

And then:

- var a = createElementFromHTML('<a href="http://example.com/">'); a.isTabStop = false; has [[TabIndexFocusFlag]] = false, [[TabIndex]] = "", and so it is *not* focusable
- var a = createElementFromHTML('<a>'); a.isTabStop = true; has [[TabIndexFocusFlag]] = true, TabIndex="", and so it *is* focusable.

It's critically important that isTabStop not be reflected as an attribute, because otherwise it does not provide an explanation for the behavior of <a href="http://example.com/">, which has its [[TabIndexFocusFlag]] set to true but does not have any isttabstop="" attribute present. Not every getter/setter on an element can be reflected as an attribute; in particular, those that control internal slots which themselves are not reflected as attributes must not be.
To unsubscribe from this group and stop receiving emails from it, send an email to blink-dev+...@chromium.org.

Domenic Denicola

unread,
Mar 11, 2015, 2:37:55 AM3/11/15
to Domenic Denicola, Simon Pieters, Daniel Freedman, 'Takayoshi Kochi (河内 隆仁)' via blink-d, Takayoshi Kochi (河内 隆仁), Anne van Kesteren, Philip Jägenstedt, Elliott Sprehn
Please disregard the below message. I was very confused and made several technical errors while writing it. (For example, I misunderstood how the "tab index focus flag" from the spec works, *and* I misunderstood the mental model behind the istabstop proposal.)

There is indeed some hidden state, implicit in the spec, regarding an element's default tab-stoppiness. Control over such state would be important for HTML as Custom Elements. But that is not really what's being discussed now. isTabStop is less about <a> vs. <a href>, and more about how tabbing to an <input type="date"> will not focus the entire control, but instead tab through the component pieces (year/month/day) making up the control.

Takayoshi Kochi (河内 隆仁)

unread,
Mar 11, 2015, 3:10:44 AM3/11/15
to Simon Pieters, Daniel Freedman, 'Takayoshi Kochi (河内 隆仁)' via blink-dev, Anne van Kesteren, Philip Jägenstedt, Elliott Sprehn
Simon,

Great question.

Our first motivation was creating a custom element (with shadow DOM) version of
<input type="date"> and give it a tab order.  If you give tabindex=N (N >= 0)
you get the expected behavior (tabbing will focus to its first element), but if you give the
same to your custom element version, the outer frame (shadow host) gets focus first when tabbed,
then goes into the inner focusable nodes second.

Then we noticed "tabindex" attribute has multiplexed meanings in it, one for declaring
the tabbing order and the other for "tab stoppiness" (in HTML spec's terminology,
"tabindex focusable flag"[1]).

Especially for shadow hosts, the decoupling is useful to separately declare the tabbing order
in the document global space while delegating the focusing into its inner focusable nodes.

And yes, the proposed spec does not add/remove anything to/from "tabindex", to keep the
compatibility of the existing web.  We expose a fraction of "tabindex" as "tabstop" to
give web authors control which was not available before.


And for <a href>, as you mentioned, it is platform-dependent (e.g. it isn't tab focusable
by default on Safari for Mac, while many others is tab focusable).  

For now let's assume <a name=""> is not focusable while <a href=""> is focusable.
In case you consider creating your own anchor element <my-anchor>, which mimics the
behavior, depending on the attribute the element becomes tab focusable or not tab focusable.
Yes, you can control by adding tabindex=0 or tabindex=-1 attribute, but then why <a>
changes the tab focusability without tabindex attribute? This is where tabstop helps.

But as Domenic pointed out in the thread, if "tabStop" *property* (of DOM object) is
reflected to "tabstop" *attribute*, it breaks...  I have to consider this case more.


Hope this clarifies your question.

I'll work on making the doc more clearer on these (Domenic's, your, or everyone else's)
questions as well as implementations.


--
Takayoshi Kochi

Simon Pieters

unread,
Mar 11, 2015, 4:56:51 AM3/11/15
to 'Takayoshi Kochi (河内 隆仁)' via blink-dev, Takayoshi Kochi (河内 隆仁), Daniel Freedman, Anne van Kesteren, Philip Jägenstedt, Elliott Sprehn
On Wed, 11 Mar 2015 08:10:20 +0100, 'Takayoshi Kochi (河内 隆仁)' via
blink-dev <blin...@chromium.org> wrote:

> Simon,
>
> Great question.
>
> Our first motivation was creating a custom element (with shadow DOM)
> version of
> <input type="date"> and give it a tab order. If you give tabindex=N (N
> >=
> 0)
> you get the expected behavior (tabbing will focus to its first element),
> but if you give the
> same to your custom element version, the outer frame (shadow host) gets
> focus first when tabbed,
> then goes into the inner focusable nodes second.

OK, thanks. Is it the responsibility of the custom element implementation
to control the tab stoppiness of its shadow host? It seems like it would
be good, so that if the custom element is not applied for whatever reason,
the fallback is still keyboard accessible. Hence, a declarative attribute
on the host element should not be used for this use case.
OK. It seems to me that something more is needed if you want to match
platform conventions for custom elements. Being able to say "have the same
focus behavior as links". Is suppose you could do <my-anchor>.tabStop = <a
href>.tabStop, though.


> Hope this clarifies your question.
>
> I'll work on making the doc more clearer on these (Domenic's, your, or
> everyone else's)
> questions as well as implementations.
>
>
> [1]
> https://html.spec.whatwg.org/multipage/interaction.html#specially-focusable

Anne van Kesteren

unread,
Mar 11, 2015, 6:06:35 AM3/11/15
to Takayoshi Kochi (河内 隆仁), Simon Pieters, Daniel Freedman, 'Takayoshi Kochi (河内 隆仁)' via blink-dev, Philip Jägenstedt, Elliott Sprehn
On Wed, Mar 11, 2015 at 8:10 AM, Takayoshi Kochi (河内 隆仁)
<ko...@google.com> wrote:
> But as Domenic pointed out in the thread, if "tabStop" *property* (of DOM
> object) is
> reflected to "tabstop" *attribute*, it breaks... I have to consider this
> case more.

This is the same problem we have with ARIA. We need to find some kind
of generic framework of attaching semantics to custom elements.


--
https://annevankesteren.nl/

Takayoshi Kochi (河内 隆仁)

unread,
Mar 11, 2015, 6:28:31 AM3/11/15
to Anne van Kesteren, Simon Pieters, Daniel Freedman, 'Takayoshi Kochi (河内 隆仁)' via blink-dev, Philip Jägenstedt, Elliott Sprehn
Anne, can I have more concrete example of the ARIA issue?
--
Takayoshi Kochi

Takayoshi Kochi (河内 隆仁)

unread,
Jun 3, 2015, 3:43:05 AM6/3/15
to blink-dev
Hi,

With much feedback from many people, I've updated the design doc.
Thanks all who have shared thoughts and experience with me.

The biggest change is that "tabStop" property on Element moved to ShadowRoot, as "delegatesFocus".

After implementing the spec on Blink, I noticed that even tabStop property is
on every Element, it has effect only on shadow hosts, which is very confusing.
So delegatesFocus should make more clear sense on shadow hosts.

With the new spec, delegatesFocus can be specified via ShadowRootInit dictionary,
which is a parameter to createShadowRoot().

Tracking bug is crbug.com/496005.

For reference, I copied a snapshot of old doc (Mar. 19):

Any comment is welcome.

--
Takayoshi Kochi
Reply all
Reply to author
Forward
0 new messages