0

Is it possible to enable JavaScript in the shadow DOM? I've tried the below and it isn't working. If not, what are the alternatives:

@Component({
  selector: 'app-website-layout',
  templateUrl: './website-layout.component.html',
  encapsulation: ViewEncapsulation.ShadowDom
})
export class WebsiteLayoutComponent implements OnInit, AfterViewInit {
    private static appendCssToShadowRoot(shadowRoot, src) {
    const link = document.createElement('link');
    link.setAttribute('rel', 'stylesheet');
    link.setAttribute('href', src);
    link.setAttribute('type', 'text/css');
    shadowRoot.prepend(link);
  }

  private static appendJs(src) {
    const script = document.createElement('script');
    script.src = src;
    document.getElementsByTagName('head')[0].appendChild(script);
  }

  ngAfterViewInit() {
    const shadowRoot: DocumentFragment = this.element.nativeElement.shadowRoot;
    WebsiteLayoutComponent.appendJs('https://code.jquery.com/jquery-3.4.1.min.js');
    WebsiteLayoutComponent.appendJs('https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js');
    WebsiteLayoutComponent.appendJs('https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js');
    WebsiteLayoutComponent.appendCssToShadowRoot(shadowRoot, 'assets/css/website.css');
    WebsiteLayoutComponent.appendCssToShadowRoot(shadowRoot, 'assets/css/page.css');
    WebsiteLayoutComponent.appendCssToShadowRoot(shadowRoot, 'assets/css/fonts.css');
  } 
}

I've also tried the below, and it doesn't work

  private static appendJsToShadowRoot(shadowRoot, src) {
    const link = document.createElement('script');
    link.setAttribute('src', src);
    shadowRoot.prepend(link);
  }

  WebsiteLayoutComponent.appendJsToShadowRoot(shadowRoot, 'assets/js/popper.min.js');
  WebsiteLayoutComponent.appendJsToShadowRoot(shadowRoot, 'assets/js/bootstrap.min.js');
  WebsiteLayoutComponent.appendJsToShadowRoot(shadowRoot, 'assets/js/jquery.min.js');

However, I can confirm that the the following does

  WebsiteLayoutComponent.appendJsToShadowRoot(shadowRoot, 'assets/js/test.js');

where the content of test.js is

alert('hello');

Stramgely enough it doesn't work for jquery and bootstrap, even though I see them in the HTML source.

enter image description here

methuselah
  • 12,766
  • 47
  • 165
  • 315
  • Try passing `shadowRoot` like you're doing for `appendCssToShadowRoot` – CertainPerformance Mar 22 '20 at 01:59
  • Thanks, it works. But strangely I can't leverage the functionality of jquery, bootstrap or popper. – methuselah Mar 22 '20 at 02:09
  • I guess the problem is that these libraries use the var document. Since the js tries to access elements directly from document and do not search for elements inside shadowroot it will not do anything to any element inside the shadow root – Gabriel G May 03 '21 at 17:37

1 Answers1

0

Since libraries rely on document root, not on shadowroot, it is needed to overwrite the document variable inside the code. I've come recently to a similiar problem, it does not include big libraries though. You can give a check if this will work for you:

function documentParse() {
   var element = document.getElementById('shadow_root').shadowRoot
   element.createElement = function createElement(type) {
      return document.createElement(type)
   }
   element.head = document.head
   return element
}

script.textContent = `(function(document) {
 //script source text
}(documentParse()));`

....

Wrapping the script in this way will bring every search on document to the scope of elements inside the shadowroot dom. You might need to clone other document functions as well.

Source: Rename var document. Run Js inside shadow root

Gabriel G
  • 97
  • 12