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

Focus trapping fails to account for elements within shadow DOM #322

Closed
elidupuis opened this issue Mar 22, 2022 · 6 comments · Fixed by #389 or #397
Closed

Focus trapping fails to account for elements within shadow DOM #322

elidupuis opened this issue Mar 22, 2022 · 6 comments · Fixed by #389 or #397
Labels

Comments

@elidupuis
Copy link

We are building a web component that uses a11y-dialog—this is similar to the framework implementations of the library but for web components.

We've noticed that focusable elements within shadow DOM are not picked up by the logic in _maintainFocus. This is because event.target.closest is not able to see into shadow DOM.

I'm wondering if you're open to a PR (and/or collaboration) that would upgrade this logic with shadow DOM support so this library can work properly with web components.

Thanks in advance!

@KittyGiraudel
Copy link
Owner

KittyGiraudel commented Mar 22, 2022

Good evening Eli!

Thank you very much for opening an issue. I am sorry to hear you are facing issues with the implementation… 😔

A pull-request would be most welcome (provided it doesn’t inflate the library size out of proportions that is). Do you have any idea how you plan on fixing it yet? I must say I know very little about web components and shadow DOM, so I’m all ears. :)

@elidupuis
Copy link
Author

Great to hear!

Admittedly, this stuff is relatively new to me as well. I've done some initial research and found https://github.com/salesforce/kagekiri, which provides "Shadow DOM-piercing query APIs". At this point, I'm assuming we could pull the relevant logic out of that library or use it as a guide for implementing the specific methods we need in a11y-dialog. I think the element.closest() logic in _maintainFocus I mentioned above is only a couple of short methods but I've only done preliminary testing on that.

That said, I'm realizing that basically anywhere a11y-dialog currently uses querySelector (or similar APIs) likely doesn't work with web components because it can't pierce the shadow DOM. I'll do a bit more digging to assess the scope of this and share my findings—hopefully within a couple of days.

@vilchesalves
Copy link

vilchesalves commented Apr 5, 2022

I was trying to use a11y-dialog with templates and slots, but was hit by this issue. I'm happy to see it here. I ended up reimplementing a lot of it, and in the meantime I wrote something that might help you as well.

/**
 * @param {string} selector
 * @param {DocumentFragment | Element} root
 * @returns {HTMLElement[]}
 */
export const piercingSelectorAll = (selector, root) => {
  const result = [];

  // matching the root against the selector
  if (root instanceof HTMLElement && root.matches(selector)) {
    result.push(root);
  }

  // matching the root's children against the selector
  const children = /** @type {HTMLElement[]} */ (
    Array.from(root.querySelectorAll(selector))
  );
  result.push(...children);

  // matching shadowRoot's children against the selector
  for (const element of result) {
    if (element.shadowRoot) {
      result.push(...piercingSelectorAll(selector, element.shadowRoot));
    }
  }

  return result;
};

    const slots = Array.from(this.querySelectorAll("slot"));

    const assignedElements = slots.flatMap((slot) =>
      slot.assignedElements({ flatten: true })
    );

    const selectableElements = Array.from(
      new Set(
        assignedElements.flatMap((element) =>
          piercingSelectorAll(focusableSelectors.join(","), element)
        )
      )
    );

So then the $$ function could be something like:

  function $$(selector, context) {
    return selectableElements.filter(element => element.matches(selector))
  }

I hope you find it useful. :)

@jbreneman
Copy link

Hello, I have the same use case--a web component that I'd like to use a11y-dialog to build. Is this being worked on by anyone? If not I'm willing to give it a go, but I don't want to duplicate effort. Thanks!

@KittyGiraudel
Copy link
Owner

Having only limited knowledge of shadow DOM, I am not personally working on this, but I’d be open to a pull-request. :)

@KittyGiraudel
Copy link
Owner

Most likely will be addressed in v8.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
4 participants