Reputation: 1072
I'm trying to find a way to encapsulate Javascript without using iframes. Ideally, I'd like a way to load external HTML components (widgets) on a parent page, without the two-step loading process, which comes with using iframes (first the host page loads, and only then does the iframe content is loaded).
Is it possible to accomplish that with some of the new web components technologies - shadow DOM / templates / imports? I was able to come close with adding HTML to shadow DOM and encapsulating CSS, but couldn't confirm whether it's possible to get a separate document for the component's javascript execution.
Upvotes: 13
Views: 7675
Reputation: 583
I've noticed that scripts running in the element, attached to the Shadow DOM are interfering with the scripts in the main page. Which was something I was trying to avoid. The scripts from the main page are not accessing the Shadow DOM, but as I noted, the shadow Dom scripts are messing up with the scripts from the main App. This unlike Iframes is not full encapsulation.
Upvotes: 0
Reputation: 155692
Yes, you can add <style>
and <script>
tags inside a template (snippet only runs in browsers that support Shadow DOM):
// Create the shadow DOM
var shadow = document.querySelector('#testOutput').attachShadow({mode: 'open'});
// Get the template fragment and add it to the shadow DOM
shadow.appendChild(document.querySelector('#testTemplate').content);
<template id="testTemplate">
<script>
alert('Hello from the component');
</script>
</template>
<div id="testOutput">shadow</div>
Or you can add them direct to the Shadow DOM.
However, this is currently broken by content security policy and is a high XSS risk.
Upvotes: 2
Reputation: 60875
This isn't related to the shadow dom, but it is related to encapsulating components entirely using javascript: https://benfrain.com/sandbox-local-htmlcss-code-snippets-inside-iframe-style-guidespattern-libraries/
Basically you create an iframe node and inject css and javascript into it:
var newIframe = document.createElement('iframe')
newIframe.contentWindow.document.open('text/html', 'replace')
var content = '<!DOCTYPE html><html><head>'+CSS+'</head><body>'+HTML+JS+'</body></html>'
newIframe.contentWindow.document.write(content)
newIframe.contentWindow.document.close()
To completely isolate the javascript from accessing its parent page, I believe you can modify the parent page's document.domain
with some kind of randomly generated domain so that, to the new iframe, the parent page will look like its in a different domain and has no way of changing its domain to match. This would come with all the usual security constraints. Then you can safely talk to the child iframe via postmessage.
Theoretically you could also implement some communication to automatically resize the iframe element depending on its content, simulating a non-iframe element in-flow.
This is something I want to experiment with in the future, but haven't tried out yet.
Upvotes: 3
Reputation: 121000
Web-components, being used through HTML imports, encapsulate both Shadow DOM HTML and related script.
To narrow the terminology, let’s consider we have a Polymer component core-ajax. Here is the code. As you might see, it does not provide any HTML markup at all, encapsulating the script only.
Once imported on the host webpage as:
<link
rel="import"
href="https://www.polymer-project.org/components/core-ajax/core-ajax.html">
this component provides an ability to perform AJAX calls without any javascript coding:
<core-ajax
auto
url="http://gdata.youtube.com/feeds/api/videos/"
params='{"alt":"json", "q":"chrome"}'
handleAs="json"
response='{{response}}'
</core-ajax>
The above will load (since auto
attribute is set) the content of the specified URL and put the response into bound response
variable. Another way to communicate with this component is to supply handler instead of binding template variable to response:
- response='{{response}}'
+ on-core-response="{{handleResponse}}"
One might implement handleResponse
function in the page’s javascript and that’s it.
UPD Although currently there is no ability to distinguish main document and one used by shadow DOM, this feature is being discussed for almost three years in w3c mailgroups. The discussion is far from conclusion, though, even in aspects like “whether we never entirely enable them in author space.”
Upvotes: 6