/"/>

关于ajax:如何执行动态加载的JavaScript块?

关于ajax:如何执行动态加载的JavaScript块?

How do you execute a dynamically loaded JavaScript block?

我正在一个网页上进行AJAX调用,该调用返回的HTML块如下:

1
2
3
  <!-- some html -->
  <script type="text/javascript">
    /** some javascript */

我正在将整个内容插入DOM,但是JavaScript没有运行。有办法运行吗?

一些细节:我无法控制脚本块中的内容(因此无法将其更改为可以调用的函数),我只需要执行整个块即可。我无法在响应中调用eval,因为JavaScript在HTML的较大块内。我可以做某种正则表达式来分离JavaScript,然后在其上调用eval,但这真令人讨厌。有人知道更好的方法吗?


通过设置元素的innerHTML属性添加的脚本不会执行。尝试创建一个新的div,设置其innerHTML,然后将此新的div添加到DOM中。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<html>
<head>
<script type='text/javascript'>
function addScript()
{
    var str ="alert('i am here');<\\/script>";
    var newdiv = document.createElement('div');
    newdiv.innerHTML = str;
    document.getElementById('target').appendChild(newdiv);
}

</head>
<body>
<input type="button" value="add script" onclick="addScript()"/>
hello world

</body>
</html>

如果您使用响应来填充div或其他内容,则不必使用正则表达式。您可以使用getElementsByTagName。

1
2
3
4
5
div.innerHTML = response;
var scripts = div.getElementsByTagName('script');
for (var ix = 0; ix < scripts.length; ix++) {
    eval(scripts[ix].text);
}

@Ed接受的答案。不适用于当前版本的Firefox,Google Chrome或Safari浏览器。我设法采用他的示例来调用动态添加的脚本。

必要的更改仅是将脚本添加到DOM的方式。代替将其添加为innerHTML的方法是创建一个新的脚本元素,然后将实际的脚本内容作为innerHTML添加到所创建的元素中,然后将该脚本元素附加到实际目标中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<html>
<head>
<script type='text/javascript'>
function addScript()
{
    var newdiv = document.createElement('div');

    var p = document.createElement('p');
    p.innerHTML ="Dynamically added text";
    newdiv.appendChild(p);

    var script = document.createElement('script');
    script.innerHTML ="alert('i am here');";
    newdiv.appendChild(script);

    document.getElementById('target').appendChild(newdiv);
}

</head>
<body>
<input type="button" value="add script" onclick="addScript()"/>
hello world

</body>
</html>

这适用于Firefox 42,Google Chrome 48和Safari 9.0.3


一种替代方法是不只是使用InnerHTML将Ajax调用的返回结果转储到DOM中。

您可以动态地插入每个节点,然后脚本将运行。

否则,浏览器仅假定您正在插入文本节点,而忽略脚本。

使用Eval是相当邪恶的,因为它需要启动Javascript VM的另一个实例并通过JIT传递字符串。


最好的方法可能是直接通过DOM识别并评估脚本块的内容。

不过,我会小心..如果您实施此操作是为了克服某些异地通话的限制,则说明您正在开设安全漏洞。

无论您实施哪种实施方式,都可以将其用于XSS。


对于那些喜欢危险生活的人:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// This is the HTML with script element(s) we want to inject
var newHtml = 'After!\
\
<'
+
  'script>\
\
changeColorEverySecond();\
\
</'
+
  'script>';
 
// Here, we separate the script tags from the non-script HTML
var parts = separateScriptElementsFromHtml(newHtml);

function separateScriptElementsFromHtml(fullHtmlString) {
    var inner = [], outer = [], m;
    while (m = /([^<]*)<\\/script>/gi.exec(fullHtmlString)) {
        outer.push(fullHtmlString.substr(0, m.index));
        inner.push(m[1]);
        fullHtmlString = fullHtmlString.substr(m.index + m[0].length);
    }
    outer.push(fullHtmlString);
    return {
        html: outer.join('\
\
'
),
        js: inner.join('\
\
'
)
    };
}

// In 2 seconds, inject the new HTML, and run the JS
setTimeout(function(){
  document.getElementsByTagName('P')[0].innerHTML = parts.html;
  eval(parts.js);
}, 2000);


// This is the function inside the script tag
function changeColorEverySecond() {
  document.getElementsByTagName('p')[0].style.color = getRandomColor();
  setTimeout(changeColorEverySecond, 1000);
}

// Here is a fun fun function copied from:
// https://stackoverflow.com/a/1484514/2413712
function getRandomColor() {
  var letters = '0123456789ABCDEF';
  var color = '#';
  for (var i = 0; i < 6; i++) {
    color += letters[Math.floor(Math.random() * 16)];
  }
  return color;
}
1
<p>Before</p>


您可以使用一种流行的Ajax库中的一种,它本身可以为您执行此操作。我喜欢原型。您可以只将evalScripts:true作为Ajax调用的一部分添加,它会自动发生。


推荐阅读