Shadow DOM:是否可以封装JS?

10
我正在寻找一种不使用iframes来封装Javascript的方法。理想情况下,我希望能够在父页面上加载外部HTML组件(小部件),而不需要使用iframes的两步加载过程(首先加载主机页面,然后才加载iframe内容)。
是否有可能通过一些新的Web组件技术 - 影子DOM / 模板 / 导入 - 来实现这一目标?我已经通过向影子DOM添加HTML并封装CSS接近了这个目标,但无法确认是否可能获得组件的JavaScript执行的单独文档。
3个回答

6

通过HTML导入使用的Web组件,封装了Shadow DOM HTML 相关脚本。

为了缩小术语范围,让我们考虑一个Polymer组件 core-ajax。这是代码。正如您所看到的,它不提供任何HTML标记,仅封装了脚本

一旦作为以下方式在主机网页上导入:

<link 
  rel="import"
  href="https://www.polymer-project.org/components/core-ajax/core-ajax.html"> 

该组件提供了一种无需任何JavaScript编码即可执行AJAX调用的能力:

<core-ajax
  auto
  url="http://gdata.youtube.com/feeds/api/videos/"
  params='{"alt":"json", "q":"chrome"}'
  handleAs="json"
  response='{{response}}'
</core-ajax>

上述代码将加载(因为设置了auto属性)指定URL的内容,并将响应放入绑定的response变量中。与此组件通信的另一种方式是提供处理程序,而不是将模板变量绑定到响应。
  - response='{{response}}'
  + on-core-response="{{handleResponse}}"

你可以在页面的javascript中实现handleResponse函数,这就是全部。

更新 尽管目前无法区分主文档和影子DOM使用的文档,但该功能正在w3c邮件组中讨论已经将近三年了。然而,讨论远未结束,甚至在“是否在作者空间中完全启用它们”等方面也存在争议。


1
谢谢@mudasobwa!通过polymer加载的脚本是否有单独的文档?我特别想将脚本的上下文分离,类似于在不同框架中运行脚本。 - Sasha
我建议你阅读W3C关于导入的规范。不,document将保持不变(除了使用iframe来创建另一个嵌入式文档外,没有其他创建嵌入式文档的能力,我敢打赌永远也不会有这样的能力)。 - Aleksei Matiushkin
谢谢!这个答案似乎暗示着将来通过Shadow DOM支持此功能,但我想知道是否已经有其他方法解决了这个问题。 - Sasha
@Sasha,我已经阅读了讨论内容,看起来我们有可能在未来看到您所请求的功能。我会更新我的答案。 - Aleksei Matiushkin
我有一个文档,现在正在尝试将其转换为Shadow DOM小部件。我引用了document.body,并希望将这些引用转换为我的小部件的最外层容器。我使用document.body进行事件处理,必须能够在页面上有多个组件实例并且在组件内部有一个组件实例。(我的组件是一个容器。)通过嵌入式JS引用我的shadow DOM的根会大有帮助。 - froggythefrog

3
这与影子DOM无关,但与完全使用JavaScript封装组件有关:https://benfrain.com/sandbox-local-htmlcss-code-snippets-inside-iframe-style-guidespattern-libraries/ 基本上,您需要创建一个iframe节点并将CSS和JavaScript注入其中:
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()

要完全隔离JavaScript以防止其访问父页面,我认为您可以使用某种随机生成的域来修改父页面的document.domain,以便对于新的iframe,父页面将看起来像在不同的域中,并且没有办法更改其域以匹配。这会带来所有常见的安全限制。然后,您可以通过postmessage与子iframe安全地通信。
从理论上讲,您还可以实现一些通信以根据内容自动调整iframe元素的大小,模拟非iframe元素的内部流动。
这是我未来想要尝试但尚未尝试的事情。

2

是的,您可以在模板中添加<style><script>标签(仅在支持Shadow DOM的浏览器中运行片段):

// Create the shadow DOM
var shadow = document.querySelector('#testOutput').createShadowRoot();

// 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>

或者,您可以直接将它们添加到Shadow DOM中。

然而,这目前由于内容安全策略而无法正常工作,并且存在高风险的XSS攻击


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接