动态添加的带有外部脚本的 javascript 不会被执行 [英] Dynamically added javascript with external script doesn't get executed

查看:37
本文介绍了动态添加的带有外部脚本的 javascript 不会被执行的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是我们的场景......我们做的第一件事是,我们附加一段 javascript 代码,将外部脚本添加到文档中,如下所示:

(function() {var e = document.createElement('script');e.type = 'text/javascript';e.async = true;e.src = 'http://blabla.com/script.js';var s = document.getElementsByTagName('script')[0];s.parentNode.insertBefore(e, s);})();

然后在 script.js 中发生以下情况:

function ajaxCall() {//... 一些发送ajax请求的脚本,成功后调用createDiv()如果(window.XMLHttpRequest){xmlhttp=新的 XMLHttpRequest();}别的{xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");}xmlhttp.onreadystatechange=function(){if(xmlhttp.readyState==4&&xmlhttp.status==200){createDiv(xmlhttp.responseText);}};xmlhttp.open("GET","http://blabla.com/api/");xmlhttp.send();}函数解析响应(响应){var parser = new DOMParser();var dom = parser.parseFromString(response, "text/html");返回dom;}函数 createDiv(responsetext){var dom = parseResponse(responsetext);var _ws = dom.getElementById('styles');var h = document.getElementsByTagName('head')[0];h.appendChild(document.importNode(_ws, true));var _jr = dom.getElementById('script1').outerHTML;var _vc = dom.getElementById('script2').outerHTML;var _rv = dom.getElementById('script3').outerHTML;var _rw = dom.getElementById('my_div');var _b = document.getElementsByTagName('body')[0];var _d = document.createElement('div');_d.id = 'just_id';_d.innerHTML = _jr + _vc + _rv;_d.appendChild(document.importNode(_rw, true));_b.appendChild(_d);}ajax调用();

一切正常,脚本和 div 正在按预期和预期的地方附加,但附加的脚本不会被执行,也不会影响附加的 div.这是为什么?我怎样才能让它执行?我们也没有在控制台中收到任何警告/错误.使用最新的 Firefox.

编辑 1

评论:您展示的示例脚本只是定义了函数,但实际上从未调用过它们

它实际上调用了一个发出ajax请求的函数,请检查编辑过的代码片段.

编辑 2

评论:您是否尝试将逻辑放在回调中,然后在 createDiv 中调用它?可能会添加一些日志来测试它是否被调用但无法找到 div

我刚刚尝试了 console.log('hello world'); 调用由 createDiv() 函数附加的脚本之一,但它什么也没做.

编辑 3

了解更多详情.当页面加载时,我可以看到

<script type="text/javascript" id="script1" src="http://blabla.com/script1.js"></script><script type="text/javascript" id="script2" src="http://blabla.com/script2.js"></script><script type="text/javascript" id="script3" src="http://blabla.com/script3.js"></script><div id="just_id">这里的内容</div>

在 DOM-Inspector 中,如上所述,有 console.log('hello world'); 来测试它,但它没有被执行.

编辑 4

通过 createDiv() 函数向文档的 <head></head> 添加了一些 CSS,它会影响我的 div,其 ID 为just_id".JS 仍然无法正常工作.

编辑 5

还尝试在我的函数中附加内联 javascript 而不是外部 javascript,但仍然没有成功.可以在 DOM-Inspector 中看到以下内容,但代码不会记录任何内容.

编辑 6

我还尝试从

第二张图是script.js的源代码,正在192.168.2.197/testing/whatever/etc上加载..htaccess 与上面的示例保持一致:

第三张图是inspector的截图,我的dom是什么样子的:

第四张图……没有任何反应.只是一个与我的问题无关的警告(我希望如此),因为即使我删除了所有脚本,它仍然会出现.

是火狐的bug吗?

解决方案

问题是上面的代码混合了 appendChildinnerHTML.在某些情况下,两者的表现不同,这并不直观.前者允许新插入的脚本节点在附加到 DOM 时执行.后者没有.这让很多人头疼不已.请谷歌script and innerHTML",你会发现面临类似问题的并不只有你一个人.

如果你(1)改变了

_d.innerHTML = _jr + _vc + _rv

_d.appendChild(document.importNode(_jr));_d.appendChild(document.importNode(_vc));_d.appendChild(document.importNode(_rv));

另外(2)变化

var _jr = dom.getElementById('script1').outerHTML;var _vc = dom.getElementById('script2').outerHTML;var _rv = dom.getElementById('script3').outerHTML;

var _jr = dom.getElementById('script1');var _vc = dom.getElementById('script2');var _rv = dom.getElementById('script3');

然后您的脚本将被执行.这是一个演示:

var b = document.getElementsByTagName('body')[0];/* 工作 */var s1 = document.createElement('script');s1.innerHTML = "console.log('我会执行')";b.appendChild(s1);/* 失败 */var s2 = document.createElement('script');s2.innerHTML = "while(1){alert('别担心!我不会执行');}";b.innerHTML = s2.outerHTML;console.log("它没有执行");

已测试

我在我的服务器上运行了 OP 的代码以尽可能地复制问题.鉴于提供的代码,我可以让它在我的服务器上按预期工作.我只将 AJAX URL 从 http://blabla.com/api/ 更改为 api.html 并提供了我自己的文件,因为 OP 没有提供任何文件,但是我从 OP 的 JavaScript 中拼凑出它应该包含的内容).以下是示例结果:

index.html

<身体><脚本>(功能() {var e = document.createElement('script');e.type = 'text/javascript';e.async = true;e.src = 'script.js';var s = document.getElementsByTagName('script')[0];s.parentNode.insertBefore(e, s);})();</html>

script.js

这是根据我上面的规范修改的 OP 代码(使用 appendChild 而不是 innerHTML,并且 AJAX 调用返回下面 api.html 的内容)

function ajaxCall() {//... 一些发送ajax请求的脚本,成功后调用createDiv()如果(window.XMLHttpRequest){xmlhttp=新的 XMLHttpRequest();}别的{xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");}xmlhttp.onreadystatechange=function(){if(xmlhttp.readyState==4&&xmlhttp.status==200){createDiv(xmlhttp.responseText);}};xmlhttp.open("GET","api.html");xmlhttp.send();}函数解析响应(响应){var parser = new DOMParser();var dom = parser.parseFromString(response, "text/html");返回dom;}函数 createDiv(responsetext){var dom = parseResponse(responsetext);var _ws = dom.getElementById('styles');var h = document.getElementsByTagName('head')[0];h.appendChild(document.importNode(_ws, true));var _jr = dom.getElementById('script1');var _vc = dom.getElementById('script2');var _rv = dom.getElementById('script3');var _rw = dom.getElementById('my_div');var _b = document.getElementsByTagName('body')[0];var _d = document.createElement('div');_d.id = 'just_id';_d.appendChild(document.importNode(_jr, true));_d.appendChild(document.importNode(_vc, true));_d.appendChild(document.importNode(_rv, true));_d.appendChild(document.importNode(_rw, true));_b.appendChild(_d);}ajax调用();

api.html(由 AJAX 调用返回)

<身体><style type="text/css" id="styles"></style><script type="text/javascript" id="script1" src="script1.js"></script><script type="text/javascript" id="script2">console.log("incline script 2")</script><script type="text/javascript" id="script3">console.log("incline script 3")</script><div id="my_div"></div></html>

script1.js

console.log("src script1");

以下是 DevTools 的结果,表明这是有效的.

So this is our scenario... First thing we do is, we append a piece of javascript code which adds external script to document like this:

(function() {
     var e = document.createElement('script'); e.type = 'text/javascript'; e.async = true; e.src = 'http://blabla.com/script.js';
     var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(e, s);
})();

Then in script.js following happens:

function ajaxCall() { 
    //... some script to send ajax request which calls createDiv() after success
   if (window.XMLHttpRequest){
      xmlhttp=new XMLHttpRequest();
   }
   else{
      xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
   }
   xmlhttp.onreadystatechange=function(){
      if(xmlhttp.readyState==4&&xmlhttp.status==200){
         createDiv(xmlhttp.responseText);
      }
   };
   xmlhttp.open("GET","http://blabla.com/api/");
   xmlhttp.send(); 
}
function parseResponse(response) {
    var parser = new DOMParser();
    var dom = parser.parseFromString(response, "text/html");
    return dom;
}
function createDiv(responsetext)
{
   var dom = parseResponse(responsetext);

   var _ws = dom.getElementById('styles');
   var h = document.getElementsByTagName('head')[0];
   h.appendChild(document.importNode(_ws, true));

   var _jr = dom.getElementById('script1').outerHTML;
   var _vc = dom.getElementById('script2').outerHTML;
   var _rv = dom.getElementById('script3').outerHTML;

   var _rw = dom.getElementById('my_div');
   var _b = document.getElementsByTagName('body')[0];
   var _d = document.createElement('div'); _d.id = 'just_id';
   _d.innerHTML = _jr + _vc + _rv;
   _d.appendChild(document.importNode(_rw, true));
   _b.appendChild(_d);
}
ajaxCall();

Everything works fine, script's and a div are being appended as expected and where expected, but appended script doesn't get executed and doesn't affect appended div. Why is that? And how can I make it execute? We also don't get any warnings/errors in console. Using latest firefox.

EDIT 1

Comment: The example script you showed just defines function, but it never actually calls them

It actually calls a function which makes an ajax request, please check edited code snippet.

EDIT 2

Comment: Have you tried to put the logic in a callback and then call it in createDiv? May be add some logs to test if its getting called but it unable to find div

I just tried console.log('hello world'); call in one of scripts which are being appended by createDiv() function, but it does nothing.

EDIT 3

For more details. When page is loaded, I can see

<script type="text/javascript" id="script1" src="http://blabla.com/script1.js"></script>
<script type="text/javascript" id="script2" src="http://blabla.com/script2.js"></script>
<script type="text/javascript" id="script3" src="http://blabla.com/script3.js"></script>
<div id="just_id">Content here</div>

In DOM-Inspector and as mentioned above, there is console.log('hello world'); to test it, but it doesnt get executed.

EDIT 4

Added some CSS through createDiv() function to document's <head></head> and it affects my div with id 'just_id'. JS still isn't working.

EDIT 5

Also tried to append inline javascript instead of external one in my function, but still no success. Can see following in DOM-Inspector, but code doesn't log anything.

<script type="text/javascript">
   console.log('test');
</script>

EDIT 6

I also tried to import nodes from as suggested here

This method is not allowed to move nodes between different documents. If you want to append node from a different document the document.importNode() method must be used.

and also tried to changed order. 1.css 2.div 3.js like this but still no success... Anyone has an idea what is going on here?

Check the code above

EDIT 7

As suggested in How do you execute a dynamically loaded JavaScript block? I set innerHTML of my div to scripts + content and then appended to DOM like this:

Check the code above

But still without any success.

EDIT 8

Added ajax function code. 3rd paramter in xmlhttp.open("GET","http://blabla.com/api/"); true (asynchronous) and false (synchronous) already tried. Still no success.

EDIT 9

function createDiv() as suggested in answer:

var dom = parseResponse(responsetext);

var _jr = dom.getElementById('script1'); //<script> tag with src attribute
var _vc = dom.getElementById('script2'); //inline <script> for test purposes only, it should actualy be external script also
var _rv = dom.getElementById('script3'); //inline <script>

var _rw = dom.getElementById('external_div');
var _b = document.getElementsByTagName('body')[0];
var _d = document.createElement('div'); _d.id = 'internal_div';
_d.appendChild(document.importNode(_rw, true));
_d.appendChild(document.importNode(_jr, true));
_d.appendChild(document.importNode(_rv, true)); //without second parameter TRUE, I dont get script content
_d.appendChild(document.importNode(_vc, true)); //without second parameter TRUE, I dont get script content
_b.appendChild(_d);

Doesn't work. Work's only if I take innerHTML of inline scripts from other document, create elements like suggested in answer, set their innerHTML and then append do current document. External script's wont load at all.

EDIT 10

.htaccess from localhost... :

<Files *.html>
    Order Deny,Allow
    Deny from all
    Allow from 127.0.0.1
</Files>

<Files *.php>
    Order Deny,Allow
    Deny from all
    Allow from 127.0.0.1
</Files>

<Files index.php>
    Order Allow,Deny
    Allow from all
</Files>

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /page1/
#RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-l
RewriteRule ^(.+)$ index.php?url=$1 [QSA,L]
</IfModule>

.htaccess from 192.*** ... :

# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /direktexpress/
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /page2/index.php [L]
</IfModule>
# END WordPress

EDIT 11

So I am probably going to give bounty @Drakes, just for the effort and his spent time on my question and going to show some screenshots what is going on. It is definitely firefox bug, I just tested on chrome and it works.

1st picture: source code of 192.168.2.197/testing/whatever/etc. .htaccess remains the same as in example above

2nd picture is source code for script.js, which is being loaded on 192.168.2.197/testing/whatever/etc. .htaccess remains the same as in example above:

3rd picture is a screenshot of inspector, how does my dom look like:

4th picture.... nothing happens. Just a warning which has nothing to do with my problem (I hope so), because even when I remove all scripts, it still appears.

Is it a firefox bug?

解决方案

The problem is that the code above is mixing appendChild with innerHTML. It's not intuitively obvious that the two perform differently in some cases. The former allows newly inserted script nodes to be executed as they are attached to the DOM. The latter doesn't. This has been a head-scratcher for many people. Kindly Google "script and innerHTML" and you will find that you are not alone facing this similar problem.

If you (1) change

_d.innerHTML = _jr + _vc + _rv

to

_d.appendChild(document.importNode(_jr));
_d.appendChild(document.importNode(_vc));
_d.appendChild(document.importNode(_rv));

and additionally (2) change

var _jr = dom.getElementById('script1').outerHTML;
var _vc = dom.getElementById('script2').outerHTML;
var _rv = dom.getElementById('script3').outerHTML;

to

var _jr = dom.getElementById('script1');
var _vc = dom.getElementById('script2');
var _rv = dom.getElementById('script3');

then your scripts will get executed. Here is a demonstration:

var b = document.getElementsByTagName('body')[0];

/* Works */
var s1 = document.createElement('script');
s1.innerHTML = "console.log('I will execute')";
b.appendChild(s1);

/* Fails */
var s2 = document.createElement('script');
s2.innerHTML = "while(1){alert('Do not worry! I will not execute');}";
b.innerHTML = s2.outerHTML;
console.log("It didn't execute");

Tested

I ran the OP's code on my server to replicate the problem as best I can. Given the supplied code I can make it work as intended on my server. I only changed the AJAX URL from http://blabla.com/api/ to api.html and supplied my own file as none was provided by the OP, but I pieced together what it should contain from the OP's JavaScript). Here are sample results:

index.html

<html lang="en">
<body>
    <script>
        (function() {
            var e = document.createElement('script'); e.type = 'text/javascript'; e.async = true; e.src = 'script.js';
            var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(e, s);
        })();
    </script>
</body>
</html>

script.js

This is the OP's code modified to my specifications above (using appendChild instead of innerHTML, and the AJAX call returns the contents of api.html below)

function ajaxCall() {
    //... some script to send ajax request which calls createDiv() after success
    if (window.XMLHttpRequest){
        xmlhttp=new XMLHttpRequest();
    }
    else{
        xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
    }
    xmlhttp.onreadystatechange=function(){
        if(xmlhttp.readyState==4&&xmlhttp.status==200){
            createDiv(xmlhttp.responseText);
        }
    };
    xmlhttp.open("GET","api.html");
    xmlhttp.send();
}
function parseResponse(response) {
    var parser = new DOMParser();
    var dom = parser.parseFromString(response, "text/html");
    return dom;
}
function createDiv(responsetext)
{
    var dom = parseResponse(responsetext);

    var _ws = dom.getElementById('styles');
    var h = document.getElementsByTagName('head')[0];
    h.appendChild(document.importNode(_ws, true));

    var _jr = dom.getElementById('script1');
    var _vc = dom.getElementById('script2');
    var _rv = dom.getElementById('script3');

    var _rw = dom.getElementById('my_div');
    var _b = document.getElementsByTagName('body')[0];
    var _d = document.createElement('div'); _d.id = 'just_id';

    _d.appendChild(document.importNode(_jr, true));
    _d.appendChild(document.importNode(_vc, true));
    _d.appendChild(document.importNode(_rv, true));

    _d.appendChild(document.importNode(_rw, true));
    _b.appendChild(_d);
}
ajaxCall();

api.html (returned by the AJAX call)

<!DOCTYPE html>
<html>
<body>
    <style type="text/css" id="styles"></style>
    <script type="text/javascript" id="script1" src="script1.js"></script>
    <script type="text/javascript" id="script2">console.log("incline script 2")</script>
    <script type="text/javascript" id="script3">console.log("incline script 3")</script>
    <div id="my_div"></div>
</body>
</html>

script1.js

console.log("src script1");

Here are the DevTools results to show this is working.

这篇关于动态添加的带有外部脚本的 javascript 不会被执行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆