将外部HTML和JS注入页面

8

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

是否有任何方法,网站管理员可以通过包含像这样的脚本来简单地加载此工具?

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

我试图通过在 AJAX 中加载它,然后在 body 上执行 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);
}

content变量包含HTML和JS,但注入时JS不会被执行。


你能否发布包含JS的content HTML代码部分? - Armen
@Armen 我编辑了这个问题。 - TheShun
2
这里有很多好的解决方案:https://dev59.com/e3NA5IYBdhLWcg3wn_U1 - phobia82
2个回答

8

由于您不知道脚本将被添加到或标签中,并因此不确定何时执行,因此需要确保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);

然而,在模板中添加<script>标签来执行一些脚本是没有意义的,因为当您添加DOM时,您的脚本已经在运行,所以请在那里进行所有必要的设置。

看起来不错,但我展示的脚本只是一个例子,在实际情况中比alert()复杂得多。我想要加载的js取决于我加载哪个模板小部件。这就是为什么我将它们全部放在一个由php文件生成的文件中。你的意思是我必须分别加载脚本和html吗? - TheShun
我的意思是,你的 widget.js 应该分成 jshtml 两部分(模板)。使用 widget.js 中的代码初始化小部件的 js 部分,而不是模板中 <script> 标签内的代码,因为在那里放置它没有意义。如果你仍然想在 <script> 标签内放置代码,请查看 这个答案,其中展示了递归函数,用于替换所有 <script> 标签的出现。 - Max Koretskyi
要使用eval,只需将js作为字符串传递eval('alert(“hello”)')。但是,由于它无法在严格的CSP下执行,因此eval()不是一个好的选择。此外,您仍然需要有单独的jshtml代码,因为eval无法可靠地与html一起工作。 - Max Koretskyi
那我需要向服务器发送两个请求吗?一个用于 HTML 模板,另一个用于 JS 脚本,对吧? - TheShun
不,只需将 js 放入 widget.js 中,并在 js 内部将模板存储在变量中。您可以开始处理它,然后提出另一个具体问题并在此处链接。 - Max Koretskyi
显示剩余2条评论

3

更新

请参考Maximus的答案。


没错,为了安全起见,默认的innerHTML不会执行JavaScript代码。

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来执行脚本? - Max Koretskyi
1
我认为OP想通过appendChild执行<script>alert('some js ')</script> - jiajianrong
好的,抱歉,我错了。实际上,不是appendChild不执行脚本,而是使用innerHTML添加的<scripts>不会被执行。appendChild可以做到。请看我的回答。 - Max Koretskyi
@Maximus,点赞你是对的,谢谢指出我的错误,无论是本地的appendChild函数还是window.eval都可以使用。 - jiajianrong

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