question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[ButtonBase] Shadow Root activeElement resolution

See original GitHub issue

When using custom elements with a shadowRoot, the focusVisibleClassName is applied to the outer-most custom element, instead of the actual element that is focused within the shadow DOM.

Expected Behavior

focusVisibleClassName should be added to deepest-level activeElement when a shadowRoot is present.

Current Behavior

focusVisibleClassName is applied to the outer-most custom element that is returned from document.activeElement.

Possible Solution

Traverse activeElement targets that have a shadowRoot to find the lowest level active element for a given component; or just return document.activeElement if no shadowRoot is present.

Following solution appears to work as expected:

diff --git a/packages/material-ui/src/ButtonBase/focusVisible.js b/packages/material-ui/src/ButtonBase/focusVisible.js
index d10c1a00d..462376f72 100644
--- a/packages/material-ui/src/ButtonBase/focusVisible.js
+++ b/packages/material-ui/src/ButtonBase/focusVisible.js
@@ -16,10 +16,11 @@ export function detectFocusVisible(instance, element, callback, attempt = 1) {
 
   instance.focusVisibleTimeout = setTimeout(() => {
     const doc = ownerDocument(element);
+    const activeElement = findActiveElement(doc);
 
     if (
       internal.focusKeyPressed &&
-      (doc.activeElement === element || element.contains(doc.activeElement))
+      (activeElement === element || element.contains(activeElement))
     ) {
       callback();
     } else if (attempt < instance.focusVisibleMaxCheckTimes) {
@@ -28,6 +29,18 @@ export function detectFocusVisible(instance, element, callback, attempt = 1) {
   }, instance.focusVisibleCheckTime);
 }
 
+function findActiveElement(doc) {
+  let activeElement = doc.activeElement;
+  while (
+    activeElement &&
+    activeElement.shadowRoot &&
+    activeElement.shadowRoot.activeElement
+  ) {
+    activeElement = activeElement.shadowRoot.activeElement;
+  }
+  return activeElement;
+}
+
 const FOCUS_KEYS = ['tab', 'enter', 'space', 'esc', 'up', 'down', 'left', 'right'];
 
 function isFocusKey(event) {

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Reactions:2
  • Comments:12 (9 by maintainers)

github_iconTop GitHub Comments

3reactions
JaiPecommented, Oct 31, 2018

Actually already got something working locally. I can just tidy it up and add some tests.

0reactions
JaiPecommented, Feb 9, 2019

Hey. You’ll probably need something like this, so the events re-target and React knows about them. React listens at the document level for events (yet events don’t bubble out of the shadow root by default).

Read more comments on GitHub >

github_iconTop Results From Across the Web

[ButtonBase] Shadow Root activeElement resolution #13447
Traverse activeElement targets that have a shadowRoot to find the lowest level active element for a given component; or just return document.
Read more >
ShadowRoot.activeElement - Web APIs - MDN Web Docs
The activeElement read-only property of the ShadowRoot interface returns the element within the shadow tree that has focus.
Read more >
Shadow DOM v1 - Self-Contained Web Components
Shadow DOM allows web developers to create compartmentalized DOM and CSS for web components.
Read more >
Finding the active element in a shadow root - A Beautiful Site
Here's a recursive function that will return the internal element that has focus, even if it's inside a shadow root. function getActiveElement( ...
Read more >
WebComponents: how to get the resolved value of a slot in ...
I have a web component and I want to adjust the value of its slot. Unfortunately, I am unable to get the resolved...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found