Search code examples
javascriptshadow-domstenciljs

Is there an issue with querying the shadow DOM of another component in StencilJS?


Example:

const breadcrumbItems = this.el.querySelectorAll('ifx-breadcrumb-item')
let label = breadcrumbItems[i].querySelector('ifx-breadcrumb-item-label')
let container = label.shadowRoot.querySelector('.breadcrumb-item-label-container')
container.classList.add('margin')

Is there an issue with this code? Is querying the shadowDOM of another component problematic, or is it a normal practice?

And if querying the shadow DOM of another component is wrong, then how do you suggest I do the above instead?

From the parent breadcrumb component, on componentDidLoad, I am getting all the nested breadcrumb-items, and then adding the class margin to the breadcrumb-item-label-container element. How else would I do that?


Solution

  • IMO, that's kind of bad practice/design. You are essentially trying to affect styling of the shadow DOM of a component from outside the component. In order for that to work, the class you are applying must be accessible by the component's shadow - which means not a global style sheet because that doesn't pierce shadow DOM. So that class would have to exist inside the component you are trying to style. But style classes used by shadow DOM should be considered "private" - not part of public API (this is the whole point of using shadow DOM).

    If you need to affect how a web component styles its shadow content, you should do it through a proper public API - in Stencil that's either @Prop or @Method.

    The exception to this is whatever might pierce shadow DOM such as inheritable properties or CSS custom properties. But the design of how your component uses those kinds of properties should allow for them to be set on the host element which therefore does not require accessing shadow DOM.

    Component design also needs to be considered. If by design the style of shadow content is meant to be determined by context (from outside usage), then perhaps it should be slotted rather that embedded within the component. Or perhaps shadow DOM is not necessary at all for a component that is mainly providing logic (like a controller) rather than style or layout. This is sometimes a problem with complex components that use other components.

    In the work that I do where various in-house shared component libraries are used, I've seen other developers occasionally try to resort to this kind of tactic in order to get around what they perceive as limitations in components (usually because they don't know any better or are too lazy to submit a change request against the component). In every case, the proper solution is to either enhance the component to support new use cases/requirements via public API, or enforce the pattern currently provided by the component (i.e. don't allow the workaround/hack for new style/behavior to be used).