简体   繁体   English

动态添加的javascript与外部脚本不会被执行

[英]Dynamically added javascript with external script doesn't get executed

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: 所以这就是我们的场景......我们首先要做的是,我们附加一段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);
})();

Then in script.js following happens: 然后在script.js发生以下情况:

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 . 一切正常, 脚本和div按预期和预期的方式被追加,但附加的脚本不会被执行,也不会影响附加的div Why is that? 这是为什么? And how can I make it execute? 我怎样才能让它执行? We also don't get any warnings/errors in console. 我们也没有在控制台中收到任何警告/错误。 Using latest firefox. 使用最新的Firefox。

EDIT 1 编辑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. 它实际上调用了一个发出ajax请求的函数,请检查编辑过的代码片段。

EDIT 2 编辑2

Comment: Have you tried to put the logic in a callback and then call it in createDiv? 评论: 您是否尝试将逻辑放入回调中,然后在createDiv中调用它? May be add some logs to test if its getting called but it unable to find div 可能会添加一些日志来测试它是否被调用但无法找到div

I just tried console.log('hello world'); 我刚试过console.log('hello world'); call in one of scripts which are being appended by createDiv() function, but it does nothing. 调用其中一个由createDiv()函数追加的脚本,但它什么也没做。

EDIT 3 编辑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'); 在DOM-Inspector中,如上所述,有console.log('hello world'); to test it, but it doesnt get executed. 测试它,但它没有被执行。

EDIT 4 编辑4

Added some CSS through createDiv() function to document's <head></head> and it affects my div with id 'just_id'. 通过createDiv()函数为文档的<head></head>添加了一些CSS,它会影响id为'just_id'的div。 JS still isn't working. JS仍然无法正常工作。

EDIT 5 编辑5

Also tried to append inline javascript instead of external one in my function, but still no success. 还尝试在我的函数中添加内联javascript而不是外部javascript,但仍然没有成功。 Can see following in DOM-Inspector, but code doesn't log anything. 可以在DOM-Inspector中看到以下内容,但代码不会记录任何内容。

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

EDIT 6 编辑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. 如果要从其他文档追加节点,则必须使用document.importNode()方法。

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? 1.css 2.div 3.js喜欢这个,但仍然没有成功......任何人都知道这里发生了什么?

Check the code above 检查上面的代码

EDIT 7 编辑7

As suggested in How do you execute a dynamically loaded JavaScript block? 您如何执行动态加载的JavaScript块中所述? I set innerHTML of my div to scripts + content and then appended to DOM like this: 我将div的innerHTML设置为scripts + content,然后像这样附加到DOM:

Check the code above 检查上面的代码

But still without any success. 但仍然没有任何成功。

EDIT 8 编辑8

Added ajax function code. 添加了ajax功能代码。 3rd paramter in xmlhttp.open("GET","http://blabla.com/api/"); xmlhttp.open("GET","http://blabla.com/api/");第3个参数xmlhttp.open("GET","http://blabla.com/api/"); true (asynchronous) and false (synchronous) already tried. 已尝试过true(异步)和false(同步)。 Still no success. 仍然没有成功。

EDIT 9 编辑9

function createDiv() as suggested in answer: 函数createDiv()如回答中所建议:

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. 只有当我从其他文档中获取内联脚本的innerHTML,创建回答中建议的元素,设置他们的innerHTML然后追加当前文档时,才能工作。 External script's wont load at all. 外部脚本根本不会加载。

EDIT 10 编辑10

.htaccess from localhost... : 来自localhost的.htaccess ...:

<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.*** ... : .htaccess来自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 编辑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. 所以我可能会给予赏金@Drakes,只是为了努力和他花在我的问题上的时间,并将显示一些截图。 It is definitely firefox bug, I just tested on chrome and it works. 它肯定是firefox bug,我刚刚测试了chrome并且它有效。

1st picture: source code of 192.168.2.197/testing/whatever/etc. 第1张图片:源代码192.168.2.197/testing/whatever/etc。 .htaccess remains the same as in example above .htaccess与上面的示例相同

源代码192.168.2.197

2nd picture is source code for script.js, which is being loaded on 192.168.2.197/testing/whatever/etc. 第二张图片是script.js的源代码,它被加载到192.168.2.197/testing/whatever/etc上。 .htaccess remains the same as in example above: .htaccess与上例中的相同:

Script.js来源

3rd picture is a screenshot of inspector, how does my dom look like: 第3张图是检查员的截图,我的dom看起来如何:

大教堂

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? 它是firefox的bug吗?

The problem is that the code above is mixing appendChild with innerHTML . 问题是上面的代码混合了appendChildinnerHTML 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. 前者允许新插入的脚本节点在附加到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. 请使用谷歌“脚本和innerHTML”,你会发现你并不是唯一面临这个类似问题的人。

If you (1) change 如果你(1)改变了

_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 另外(2)改变

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. 我在我的服务器上运行了OP代码以尽可能地复制问题。 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). 我只是将AJAX URL从http://blabla.com/api/更改为api.html并提供了我自己的文件,因为OP没有提供,但是我将它应该包含在OP的JavaScript中。 Here are sample results: 以下是示例结果:

index.html 的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 的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) 这是根据我上面的规范修改的OP代码(使用appendChild而不是innerHTML ,AJAX调用返回下面api.html的内容)

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) api.html (由AJAX调用返回)

<!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 script1.js

console.log("src script1");

Here are the DevTools results to show this is working. 以下是DevTools的结果,表明这是有效的。

开发工具检查

控制台结果

The JavaScript of the page has already been parsed, you need to eval the result to put it into the window context. 页面的JavaScript已经被解析,您需要eval结果以将其放入窗口上下文中。

function evalRequest(url) {
    var xmlhttp = new XMLHttpRequest();
    xmlhttp.onreadystatechange = function() {
        if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
            eval(xmlhttp.responseText);
        }
    }
    xmlhttp.open("GET", url, true);
    xmlhttp.send(null);
}; 

UPDATE : you might need to get the script as text and then eval it ) 更新:您可能需要将脚本作为文本然后评估它)

function getScriptsAsText() {
    var div = document.createElement('div');
    var scripts = [];
    var scriptNodes = document.getElementsByTagName('script');

    for (var i = 0, iLen = scriptNodes.length; i < iLen; i++) {
        div.appendChild(scriptNodes[i].cloneNode(true));
        scripts.push(div.innerHTML);
        div.removeChild(div.firstChild);
    }
    return scripts;
};

You are currently loading a script into your page. 您当前正在将脚本加载到页面中。 This happens AFTER the page js was already executed (since is async and usually loading an external resource takes longer than the script execution). 这是在页面js已经执行之后发生的(因为它是异步的,并且通常加载外部资源比脚本执行花费的时间更长)。

So... basically you have the right function definitions and everything, and calling them from console later should work (I think you already tried that and was fine). 所以...基本上你有正确的功能定义和所有东西,稍后从控制台调用它们应该工作(我想你已经尝试过并且很好)。

Still, the calls to your functions, even thought they are there won't execute since will not be computed after load. 仍然,对函数的调用,即使认为它们在那里也不会执行,因为加载后不会计算。

To work around this, you can create an ajax request to a page that has the script, and in its callback to call whatever function you need to call. 要解决此问题,您可以为具有脚本的页面创建ajax请求,并在其回调中调用您需要调用的任何函数。

我会说删除异步,因为操作在初始页面加载后出现

    (function () {
        var s = document.getElementsByTagName('script')[0];
        s.parentNode.insertBefore(CreateScriptTag('http://blabla.com/script.js',true), s);
    })();
    function CreateScriptTag(src, isAsync) {
        var e = document.createElement('script')
        e.type = 'text/javascript';
        e.async = isAsync;
        e.src = src;
    return e;
    }
    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.open("GET", "http://blabla.com/api/");
        xmlhttp.onreadystatechange = function () { if (xmlhttp.readyState === 4 && xmlhttp.status === 200) createDiv(xmlhttp.responseText); };
        xmlhttp.send();
    }
    function parseResponse(response) {
        var parser = new DOMParser();
        var dom = parser.parseFromString(response, "text/html");
        return dom;
    }
    function createDiv(responsetext) {
        var head=document.getElementsByTagName('head')[0],dom = parseResponse(responsetext),_div=document.createElement('div');
        head.appendChild(document.importNode(dom.getElementById('styles'), true)); //Assuming stylesheets are working fine.
        _div.id = 'just_id';
        //div.innerHTML = _script1 + _script2 + _script3;
        document.getElementsByTagName('body')[0].appendChild(_div.appendChild(dom.getElementById('my_div')));

        head.appendChild(CreateScriptTag(dom.getElementById('script1').getAttribute("src"),true));
        head.appendChild(CreateScriptTag(dom.getElementById('script2').getAttribute("src"),true));
        head.appendChild(CreateScriptTag(dom.getElementById('script3').getAttribute("src"),true));

    /* or another idea;
        document.write(_script1 + _script2 + _script3);
    */
    }
    ajaxCall();

The problem is your method is asynchronous not synchronous execution. 问题是你的方法是异步而不是同步执行。 Meaning the page is still loading while you insert your dynamical scripts. 这意味着在插入动态脚本时页面仍在加载。 There are few ways to do so: 有几种方法可以这样做:

  1. The easiest way is to use jQuery $(document).append instead of document.createElement, if you don't mind using the library. 最简单的方法是使用jQuery $(document).append而不是document.createElement,如果你不介意使用库。

  2. You can also use jQuery $.getScript . 您还可以使用jQuery $.getScript

  3. If you prefer not to include 3rd libraries, you have to think of a way to delay page load while you insert your scripts, something like an alert to creat a prompt window will work, but user experience not so good, think of a more smooth way. 如果您不想包含第3个库,则必须考虑在插入脚本时延迟页面加载的方法,例如创建提示窗口的alert会起作用,但用户体验不太好,想想更顺畅方式。

  4. Use XMLHTTPRequest to retrieve your scripts. 使用XMLHTTPRequest检索脚本。 This is synchronous and I can see you are familiar with it because you are using it in your script already. 这是同步的,我可以看到你熟悉它,因为你已经在脚本中使用它了。

Good luck and happy coding. 祝你好运,编码愉快。

Have you tried getting the URL of the src file and excuting it with jQuery's getScript() function? 您是否尝试过使用jQuery的getScript()函数获取src文件的URL并将其getScript()

Change your creatediv() function : 更改您的creatediv()函数:

var _jr = dom.getElementById('script1').outerHTML;

var srcScript1Left = _jr.split('src="');
srcScript1Right = srcScript1Left[1].split('"');
srcScript1 = srcScript1Right[0];

console.log(srcScript1);

$.getScript(srcScript1, function()
{
    // The external script has been executed.
});

EDIT 编辑

Similar method in pure Javascript : 纯Javascript中的类似方法:

var _jr = dom.getElementById('script1').outerHTML;

var srcScript1Left = _jr.split('src="');
srcScript1Right = srcScript1Left[1].split('"');
srcScript1 = srcScript1Right[0];

console.log(srcScript1); // Display the name of the external script file

if(window.XMLHttpRequest) {
    xmlhttp=new XMLHttpRequest();
} else {
    xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange=function() {
    if(xmlhttp.readyState == 4 && xmlhttp.status == 200) {
        eval(xmlhttp.responseText); // Execute the external script
    }
};
xmlhttp.open("GET",srcScript1);
xmlhttp.send();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM