【问题标题】:Inject external HTML & JS into a page将外部 HTML 和 JS 注入页面
【发布时间】:2016-11-22 15:59:15
【问题描述】:

我正在尝试构建一些网站管理员可以嵌入到他们网站中的小部件工具。

网站管理员有什么方法可以通过包含这样的脚本来简单地加载此工具?

<script src="http://mysite/widget.js"></script>

我尝试通过在 AJAX 中加载它来注入它,然后在主体上执行 appendChild(),它以这种方式工作但 JS 没有执行。

要注入的内容:

<div>one div</div>
<script>alert('some js')</script>

widget.js

function load(content) {
  var $body = document.body,
      $div = document.createElement("div");
  $div.id = "widget";
  $div.innerHTML = content; 
  $body.appendChild($div);
}

内容变量包含HTML和JS,但JS在注入时没有执行。

【问题讨论】:

标签: javascript html


【解决方案1】:

由于您不知道脚本将被添加到哪里 - 到 head 或 body 标记 - 因此何时执行,您需要确保 DOM 已准备好,所以:

 function load(content) {

    if (document.readyState === "complete" || document.readyState === "loaded") {
         // manipulate DOM
    } else {
       document.addEventListener('DOMContentLoaded', function() {
        // manipulate DOM
    })
}

此外,由于使用 innerHTML 添加的脚本元素不会被执行,因此您必须使用 eval 或更好地创建这样的脚本:

var s = document.createElement('script');
s.text = "alert(\"hi\");"
document.getElementsByTagName('head')[0].appendChild(s);

但是,添加 &lt;script&gt; 标记来执行模板中的某些脚本是没有意义的,因为在添加 DOM 时脚本已经在运行,所以在那里进行所有必要的设置。

【讨论】:

  • 看起来不错,但我展示的脚本只是一个示例,实际上它比 alert() 复杂得多。我要加载的 js 取决于我加载的模板小部件。这就是为什么我将所有内容合二为一,由一个 php 文件生成。你的意思是我必须分别加载脚本和html?
  • 我的意思是你的widget.js 应该分成jshtml 部分(模板)。使用 widget.js 内的代码初始化小部件的 js 部分,而不是模板的 &lt;script&gt; 标记内的代码,因为将它放在那里没有意义。如果您仍然希望将代码包含在 &lt;script&gt; 标记中,请检查 this answer,它显示了将所有出现的 &lt;script&gt; 标记替换为 createElement('script') 的递归函数。
  • 要使用eval,只需将js 作为字符串eval('alert("hello")') 传递。但是,eval() 不是一个好的选择,因为它不会在严格的CSP 下执行。此外,您仍然必须拥有单独的 jshtml 代码,因为 eval 不能可靠地与 html 一起工作
  • 所以我必须向服务器发出 2 个请求?一个用于 html 模板,一个用于 js 脚本,对吗?
  • 不,只需将js 放入widget.js,并将模板存储在js 中的变量中。您可以开始研究它,然后提出另一个具体问题并将其链接到此处
【解决方案2】:

更新

请看马克西姆斯的回答。


对,为了安全起见,默认的原生 innerHTML 不会执行 js。

jQuery 或 zepto $.fn.append() 可以满足你的需要,原因如下(来自 zepto 源码)

if (el.nodeName != null && el.nodeName.toUpperCase() === 'SCRIPT' &&
           (!el.type || el.type === 'text/javascript') && !el.src)
          window['eval'].call(window, el.innerHTML)

您可以在函数 $.fn.append 中看到它会查看它是否是脚本,如果是,它将使用eval 来运行内容。您可能需要eval,但要小心,因为某些网站管理员可能会提供“副”代码

【讨论】:

  • OP 在哪里使用appendChild 执行脚本?
  • 我认为 OP 想让 &lt;script&gt;alert('some js ')&lt;/script&gt; 被 appendChild 执行
  • 好的,抱歉,错过了。实际上,不是appendChild 不执行脚本,而是&lt;scripts> 加上innerHTML 不执行。 appendChild 可以做到。看我的回答。
  • @Maximus,投票你是对的,谢谢你指出我的错误,原生 appendChild 函数或 window.eval 都有效。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-05
相关资源
最近更新 更多