I’ve been working with https://lit.dev elements and want to summarize what I’ve learned about Shadow DOM and Focus:

  • When a custom element is created with document.createElement('my-el') it’s constructor is run. It has an ownerDocument, but isConnected is false and shadowRoot is null.
  • When the element is appended to a DOM element it becomes connected to the document. isConnected will be true and the connectedCallback is enqueued by the browser, even if it may not run immediately. A call to getRootNode({composed: true}) will return the document.
  • When attachShadow is called, the shadowRoot will be initialized and take over the elements place in the render tree. That means that past this point, any element in the light dom that’s not assigned to a slot will not have any offsetParent, which means that the element has no render box and is not considered being rendered.
  • The element being rendered is one of the requirements for being a focusable area:
    1. the element’s tabindex value is non-null, or the element is determined by the user agent to be focusable;
    2. the element is either not a shadow host, or has a shadow root whose delegates focus is false;
    3. the element is not actually disabled;
    4. the element is not inert;
    5. the element is either being rendered, delegating its rendering to its children, or being used as relevant canvas fallback content.

Thus, if you want to call focus on an element in light dom of a custom element and have it actually move the focus and dispatch the focus event, ensure all the shadow dom slots are initialized.