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

Tab Across Web Components #50

Closed
scottfr opened this issue Mar 25, 2020 · 11 comments · Fixed by #570
Closed

Tab Across Web Components #50

scottfr opened this issue Mar 25, 2020 · 11 comments · Fixed by #570
Projects

Comments

@scottfr
Copy link

scottfr commented Mar 25, 2020

It would be great if tabbable could identify the tab ordering between input fields across multiple web components.

Unfortunately since query selectors can't span shadow roots, it may require a very different approach.

@LohitakshTrehan
Copy link

I agree with @scottfr, currently, even $(":tabbable") selector of jquery-ui doesn't support web components. So it would be great if we can add support for it (of course only in the case where "mode" is open).

It would be interesting to find an approach to this problem. For me personally a solution can work, where I iterate over the DOM nodes and an API could tell me, whether that Node is tabbable or not.

@stefcameron
Copy link
Member

For me personally a solution can work, where I iterate over the DOM nodes and an API could tell me, whether that Node is tabbable or not.

This library does now have isTabbable() and isFocusable() APIs that might help.

@idoros
Copy link
Contributor

idoros commented Apr 6, 2021

Unfortunately current solution is not enough as Tabbable code doesn't deal with the concept of shadow dom at all.

This is what needs to be solved as far as I can see:

Query shadow roots

  • locate elements with shadow roots
  • query internal shadow doms recursively and insert potential nested elements in the correct order into the candidate list
  • reorder slotted elements by their slot position and make sure to test them by their slot ancestor instead of parentElement
  • sort light/shadow dom separately by tab index (need to check what happens with slotted light dom elements)

The problem is that as far as I know, there is no CSS selector to query elements that has a shadow root. That means that a search needs to iterate on every element to check for .shadowRoot property and then query it internally. We could provide an attribute to mark elements with shadow root (e.g. data-tabbable-root), and a utility function to search and mark such elements, allowing a user to mark them themselves or run the function as needed.

edit: we can also offer an option to specify custom element names to simplify the search (in case you know the element list in your application).

Filter elements within shadow dom

  • parentElement of top level elements in a shadow root is null - need this to figure out if an element is displayed (maybe parentNode.host)
  • when searching for other radio elements with no containing form, the fallback should be to the shadow root and not the document (if one exist)

other

  • shadow root elements with delegatesFocus: true are probably positive for isTabbable/isFocusable, but might need to be excluded from tabbable/focusable list (always or only when they have internal focusable content?)

No matter the solution, currently Tabbable is tested with jsdom and implementing this without testing in a real browser is problematic.

@LohitakshTrehan
Copy link

The approach we followed was BFS traversal of the tree if there is a shadow root element. We used jQuery $(:tabbable) selector on each element to find if it is tabbable or not and pushed it in array if it is a tabbable element. After that we compared the index of current focused element to the next tabbable element in array. and transfer the focus there. If it was the last, then we started on first array element.
It used a lot of computation. But we needed that feature in our project. There was no visible delay for user.

@idoros
Copy link
Contributor

idoros commented Apr 6, 2021

BFS traversal of the tree if there is a shadow root element

That is what I was suggesting, but I wouldn't run this by default for every tabbable/focusable call as not all users use shadow-dom (at list not in the current major version, maybe with a flag that can be flipped in the future).

There was no visible delay

That depends on the size and complexity of the DOM you have, the device your code is running on, and how frequent you run your checks.

@LohitakshTrehan
Copy link

Yes we can give a flag.
Your point about performance issue is correct. But our application was fairly complex (around 80k lines of DOM code scanned), mostly run on PCs, and we didn't use it frequently. So for our use case it was perfect.

@idoros
Copy link
Contributor

idoros commented Apr 9, 2021

@LohitakshTrehan did you handle slotted elements in your implementation?

@LohitakshTrehan
Copy link

LohitakshTrehan commented Apr 9, 2021

No we didnt add any support for slotted elements at that time as we were not using them. Slotted elements are present out in the light-dom, so can be scanned easily even using jQuery $(:tabbable) . But determining their position with respect to their parent in DOM can be tricky. Even I have to try it, before confirming.

@LohitakshTrehan
Copy link

https://stackoverflow.com/a/54119840
Found this, maybe helpful.

@stefcameron
Copy link
Member

The problem is that as far as I know, there is no CSS selector to query elements that has a shadow root. That means that a search needs to iterate on every element to check for .shadowRoot property and then query it internally. We could provide an attribute to mark elements with shadow root (e.g. data-tabbable-root), and a utility function to search and mark such elements, allowing a user to mark them themselves or run the function as needed.

@idoros Thanks for the proposal/ideas. I like your idea of supporting a custom data-tabbable-root attribute which we can add to our CSS queries to find all open shadow DOMs we'd need to scan. Providing a utility function to scan the DOM and add this attribute to any node with an open (element.shadowRoot !== null) shadow would be nice-to-have, could be added in the future if necessary.

You clearly have more experience with the shadow DOM than I do. I'm trying my best to catch-up, but relying on your knowledge to implement something that will make sense.

Slotted elements are a challenge, but what you're proposing sounds right. We could immediately start today by excluding from the order any elements we find to be tabbable if they have a slot attribute, which would take care of both open and closed shadow DOMs. We should then find them again when we introspect the shadow DOM they belong to (if it's open), and insert them in the right order WRT to their position within the shadow DOM as opposed to light DOM.

@idoros idoros mentioned this issue Apr 23, 2021
8 tasks
@stefcameron
Copy link
Member

FYI, tabbable@beta has support for shadow DOM. Check it out! Also in focus-trap@beta.

@stefcameron stefcameron added this to In Progress in 5.3.0 Mar 12, 2022
@stefcameron stefcameron moved this from In Progress to Done in 5.3.0 Mar 12, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
No open projects
5.3.0
Done
Development

Successfully merging a pull request may close this issue.

4 participants