3

I am working with the current PWA starter kit template from Polymer.

https://github.com/Polymer/pwa-starter-kit/tree/template-typescript

My web-component element page return the following code with a DIV element:

return html`
      ${SharedStyles}    
      <section>
        ...my content...

      </section>
      <div id="CONTAINER NAME"></div>
    `

I need to get access to the CONTAINER NAME element from an external javascript via document.getElementById. I know that it sit's in the shadow dom but I cant change the external JS. So question is how to make it accessible from JS via document.getElementById?

The external JavaScript loads an iframe into the named div. This external component needs to get the div element by document.getElementById to load the iframe into the specified div.

I have searched and haven't found a way to force the div element from the shadow dom of my web-component page to be exposed/placed in the DOM.

I just found this solution mentioned here but I didnt get it to work in the PWA template. Maybe as the shadow doms are cascaded in the PWA template?

https://stackoverflow.com/a/47082470/9192527

Is there any way I can update the web component based on the Polymer v3/PWA kit with the external javascript (third party) still using document.getElementbyId to modify my div inside the web component?

So looking for a possibility using maybe slots to expose an element of the shadow dom to the light dom? But cant get it to work with the solutions linked above.

datapool
  • 203
  • 3
  • 12

3 Answers3

3

As you mentioned, lit-element uses shadow dom by default as it has a lot of advantages when you're making reusable components.

However, you can opt out of it and use "light" dom which will render the component's content in the main dom which in turn will make that content accessible using things like document.getElementById() or document.querySelector()

Here's a minimum example of how to do use light dom in a lit-element-based component (and here's a glitch where you can see it in action)

class MyElement extends LitElement {  
  render() {
    return html`
      <section>
        ... My content ...
      </section>
      <div id="myElementContainer">
        This is a container inside my element
      </div>
    `;
  }

  createRenderRoot() {
    // this is what overrides lit-element's behavior so that the contents don't render in shadow dom
    return this;
  }

}

Just bear in mind that in this case you should only use this component once in your application because of how getElementById() works and that you won't be able to use <slot> in your component as that's a feature only available for shadow dom

Alan Dávalos
  • 2,568
  • 11
  • 19
  • This solution worked. However, I had to put the `createRenderRoot` also in the parent of the component. After that it works. However, I have now ruined my styles template from the parent component as the style variables are not populated to the child elements anymore. Must be related to the createRenderRoot on the parent? ? – datapool Nov 07 '18 at 20:58
  • Yes, I think I read somewhere that using light dom basically meant you have to style using "normal" css, for this case you could add those styles as a global style in your html I guess – Alan Dávalos Nov 07 '18 at 21:35
  • I just updated the glitch to show how you can handle styling for this case – Alan Dávalos Nov 07 '18 at 21:41
  • That did it! Just putting the style variables into the normal html solved the styling topic. Thank you! – datapool Nov 07 '18 at 21:44
2

So looking for a possibility using maybe slots to expose an element of the shadow dom to the light dom? But cant get it to work with the solutions linked above.

Actually what you should do is the opposite: expose the <div> to be selected in the light DOM and integrate it in the Shadow DOM via a <slot> tag if you need.

this.innerHTML = '<div id="CONTAINER NAME"></div>'
return html`
      ${SharedStyles}    
      <section>
        ...my content...

      </section>
      <slot></slot>
    `
Supersharp
  • 29,002
  • 9
  • 92
  • 134
  • This solution also worked. But I also had to add createRenderRoot() into the element to get this working. – datapool Nov 07 '18 at 21:52
0

You can also do it programmatically via slotting

  1. Add .setAttribute('slot','slot-inside-your-shadow-dom') to your element that needs to be queried via .getElementById()
  2. Append your element somewhere inside document (i.e. document.appendChild(element) )

Now your element should appear inside the shadowDom slot, but it's also possible to query it via document.querySelector (etc.)

Blitzlord
  • 241
  • 2
  • 9