Reputation: 21694
I've been trying to figure this out for hours to no avail.
Basically, I'm making a Chrome Extension that loads a bunch of HTML into the page. To prevent a lot of issues, I wanted to use Shadow DOM for encapsulation. CSS encapsulates fine, and after a while I finally managed to load JS into the DOM as well.
Problem is, I'm including 2 JS files:
The problem is, after loading the vendor.js
file, I can't access things it applied inside the other script.
For the life of me I can't figure out which context it signs into and uses as a "window" element to inject classes into.
Here's my Shadow DOM initial loading script:
// HACK: we need to "bleed" font-faces to outside the shadow dom because of a bug in chrome
document.querySelector('head').innerHTML +=
'<link href="//maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css" rel="stylesheet" />'
let rootElHtml = `
<div id="cls-root">
<template id="cls-template">
</template>
</div>
`
document.body.innerHTML += rootElHtml
let rootEl = document.querySelector('#cls-root'),
templateEl = rootEl.querySelector('template'),
shadow = rootEl.attachShadow({
mode: 'open'
})
let cssLinks = [
chrome.extension.getURL('dist/css/vendor.css'),
chrome.extension.getURL('dist/css/extension.css'),
'//maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css'
].map(p => `<link rel="stylesheet" href="${p}" />`).join("\n")
let jsLinks = [
chrome.extension.getURL('dist/js/vendor.js'),
chrome.extension.getURL('dist/js/extension.js'),
].map(p => `<script src="${p}" type="text/javascript"></script>`).join("\n")
// inject css/js into template content
let templateNodeContents = document.createElement('div')
templateNodeContents.innerHTML = [cssLinks, jsLinks].join("\n")
for (let node of templateNodeContents.childNodes)
templateEl.content.appendChild(node)
// load remote HTML template and apply into shadow root template
fetch(chrome.extension.getURL('dist/templates/root.html')).then((data) => {
data.text().then((body) => {
let bodyContainer = document.createElement('div')
shadow.appendChild(document.importNode(templateEl.content, true))
bodyContainer.innerHTML = body
shadow.appendChild(bodyContainer.childNodes[0])
})
})
window._clsExt = shadow
And here's me trying to bootstrap Angular:
const shadow = document.querySelector('#cls-root').shadowRoot
shadow.APP_NAME = 'myApp'
shadow.app = angular.module(APP_NAME, ['ngStorage'])
.config(['$localStorageProvider', '$httpProvider',
($localStorageProvider, $httpProvider) => {
$localStorageProvider.setKeyPrefix('myApp');
$httpProvider.interceptors.push(apiInterceptor);
}
])
shadow.app.constant('APP_ENV', APP_SETTINGS.app_env)
shadow.app.constant('API_BASE', APP_SETTINGS.api_base_url)
angular.bootstrap(shadow, [APP_NAME])
However, I keep encountering errors about angular:
extension.js:21 Uncaught ReferenceError: angular is not defined
at extension.js:21
I tried looking around inside the properties of the shadow element, as well as other window
variations and attempting to find the document inside.
Is this possible without combining the files together by force? I'd like to have this separation remain.
Upvotes: 4
Views: 2785
Reputation: 584
I see that you add JS files into the Shadow DOM scope. It doesn't work as accessing the elements in Shadow dom is different and the libraries don't work inside them.
When I say accessing DOM is different
$(#id) //the usual way to access a id using jQuery
parentofShadowtree.shadowRoot.querySelector('#id') //to select a element inside shadow DOM
As you can see accessing DOM elements inside Shadow DOM is different, the usual JS library files can't access the elements and don't work here
Correct me if I am wrong.
Upvotes: 1