这是indexloc提供的服务,不要输入任何密码
Skip to content

Conversation

@cdata
Copy link
Contributor

@cdata cdata commented May 1, 2019

Shadow DOM support

This change enables custom elements to coordinate with the polyfill in order to apply it to discrete scopes of the composed tree (such as their shadow roots). Highlights include:

  • The polyfill is now exported to window as a function that can be invoked on any Document|ShadowRoot
  • The polyfill is automatically, synchronously applied to the top-level document when its script runs, preserving the existing behavior
  • Code that wishes to use the polyfill in a ShadowRoot can apply it as needed
    • Shadow hosts that have the polyfill applied are decorated with a data-js-focus-visible attribute
  • A focus-visible-polyfill-ready event is now dispatched on window to support lazy loading scenarios (see discussion in Is DOM ready necessary? #193)

Example usage as a mixin

In the course of implementing this proposal, I also crafted a mixin to demonstrate how a custom element should coordinate with the polyfill in order to take advantage of :focus-visible-like capabilities.

I am not proposing to include the mixin in the main project, but it is linked here for future reference:

https://github.com/cdata/focus-visible/blob/fc33395294dd69ad8af70e822e6176a8893816e2/src/custom-element-mixin.js#L1-L49

Compared to #112

There is an older WIP proposal (#112) up for consideration that attempts to add transparent support for arbitrary shadow roots. This approach would ultimately require that focus and blur be observed at every shadow boundary throughout the composed tree in order to work transparently.

The design in the proposal before you expects a Shadow DOM-using component author to opt-in to the polyfill. This allows the polyfill to be applied to Shadow DOM-using components that need it, while avoiding the cost of observing changes in shadow roots that do not know about or do not want to use the polyfill.

Examples

For the purposes of the following examples:

  • :focus elements are styled with purple color and border-color
  • :focus-visible elements receive the default focus ring
  • Elements with shadow roots have delegatesFocus: true configured
  • Custom elements apply the mixin referenced above to coordinate with the polyfill
This change w/ pointer traversal This change w/ keyboard traversal
focus-not-visible focus-visible

Shadow root styling problem

Without this change, it is not possible to use the polyfill from within a shadow root. In the following example, you can see that the top-level document elements can be styled to hide the focus ring when focusing with pointer events, but focusable elements inside of a shadow root still display a focus ring:

Failure to style shadow root-encapsulated elements
focus-not-visible-broken

Fixes #193
Fixes #28
Fixes #112

@cdata cdata marked this pull request as ready for review May 1, 2019 01:11
@cdata cdata changed the title (WIP) Shadow DOM support Shadow DOM support May 1, 2019
@cdata cdata force-pushed the shadow-dom-support branch from 62178c9 to f65c666 Compare May 1, 2019 01:46
This change enables custom elements to coordinate with the polyfill in
order to apply it to discrete scopes of the composed tree (such as
shadow roots).
@cdata cdata force-pushed the shadow-dom-support branch from f65c666 to ba3bd97 Compare May 1, 2019 01:47
loaded = false;
document.addEventListener('DOMContentLoaded', load, false);
window.addEventListener('load', load, false);
// We detect that a node is a ShadowRoot by ensuring that it is a
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Putting this comment inside the previous if block is breaking my brain a little bit, though I can see why you did. Maybe switch the order of these if/else statements? It would be marginally less efficient for the "normal" case, I guess, but more readable.

Or, perhaps a switch statement over scope.nodeType ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @alice I'll try to rework this so that it is less of an eyesore

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I switched the order of the conditions around. The comments are hopefully more cleanly formatted now.

return matchesMouse(false);
});

describe('focusable elements in shadow', () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like this test is failing in CI for some reason.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry about that, will take a look

@alice
Copy link
Member

alice commented May 1, 2019

Generally looking really good (that gif blew my mind), not sure what's going on with that test though.

@alice
Copy link
Member

alice commented May 8, 2019

Just had a quick look into the test failure. Looks like it's failing in Firefox due to some interaction between the shady dom polyfill and the focus-visible polyfill.

@cdata cdata force-pushed the shadow-dom-support branch from f5b2c46 to 8fe026b Compare May 12, 2019 21:35
@cdata
Copy link
Contributor Author

cdata commented May 12, 2019

I have addressed the test failures. The following changes were necessary:

  • In some browsers, we accessed the content of the inline <template> in the Shadow DOM test fixture too early. Deferring until DOMContentLoaded fixed the issue.
  • I had (mistakenly) assumed that the polyfill did not need to be applied at shadowRoot boundaries when the ShadyCSS polyfill was in effect. But, it turns out the polyfill does encapsulate focus events at the virtual shady root boundaries that it creates, so the :focus-visible polyfill should still be applied as normal.

@cdata cdata force-pushed the shadow-dom-support branch from 8fe026b to 994b3b2 Compare May 13, 2019 00:44
@alice
Copy link
Member

alice commented May 14, 2019

Aww, no commit history? We squash merge anyway. I can't tell what's changed since I last looked :\

@cdata
Copy link
Contributor Author

cdata commented May 14, 2019

Sorry, I will try to recreate the history

@alice
Copy link
Member

alice commented May 14, 2019

Thanks and sorry :\

@cdata cdata force-pushed the shadow-dom-support branch from 994b3b2 to a3f1f3d Compare May 14, 2019 17:10
@cdata
Copy link
Contributor Author

cdata commented May 14, 2019

Nothing a little git gymnastics couldn't handle 👍

I split the change out into two commits. It looks like the Node.js 10 build had three test timeouts this time around. I'm not sure if that is actionable...

} else if (scope.nodeType === Node.DOCUMENT_FRAGMENT_NODE && scope.host) {
// We detect that a node is a ShadowRoot by ensuring that it is a
// DocumentFragment and also has a host property. This check covers native
// implementation and polyfill implementation transparently. If we only cared
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the added counterfactual here, thank you!

if (self.ShadyCSS == null || self.ShadyCSS.nativeShadow) {
applyFocusVisiblePolyfill(el.shadowRoot);
}
}, { once: true });
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TIL 😲
Since 2016, even!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎉

@alice
Copy link
Member

alice commented May 15, 2019

Thanks so much for the git gymnastics! Build problems look like they were an infrastructure problem, it passed once I restarted it.

@cdata
Copy link
Contributor Author

cdata commented May 15, 2019

Thanks @alice ! BTW I should note before anyone merges this that due to the fix for #196 this should probably be considered a breaking change (albeit a small one). The timing around which the polyfill activates is different now, so it could break any user who is sensitive to that.

@cdata
Copy link
Contributor Author

cdata commented May 29, 2019

@robdodson LMK if I can do anything to help move this one forward!

image

@robdodson robdodson self-requested a review June 4, 2019 20:15
Copy link
Collaborator

@robdodson robdodson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implementation looks good to me, thank you for writing the tests!

I just had a couple of comments regarding the README.

README.md Outdated

```javascript
// Check for the polyfill:
if (self.applyFocusVisiblePolyfill != null) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is self equivalent to window? Not sure if the snippet makes it clear what context this is running in.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, self is equivalent to window in the document context. I can change this to window.

README.md Outdated

```javascript
window.addEventListener('focus-visible-polyfill-ready',
() => self.applyFocusVisiblePolyfill(myComponent.shadowRoot),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to above, what's the expected value of self in this context?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will change to window.

@robdodson
Copy link
Collaborator

I think this is good to merge once chris makes the window changes in the README.

@cdata
Copy link
Contributor Author

cdata commented Jun 6, 2019

@robdodson README has been updated per your request 🍻

@robdodson robdodson merged commit 44c0db8 into WICG:master Jun 6, 2019
try {
event = new CustomEvent('focus-visible-polyfill-ready');
} catch (error) {
// IE11 does not support using CustomEvent as a constructor directly:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it support new Event()?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some very light Googling yields this Stack Overflow that suggests "no" and "use CustomEvent instead" 😄

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Is DOM ready necessary? Shadow DOM

4 participants