簡體   English   中英

動態添加的javascript與外部腳本不會被執行

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

所以這就是我們的場景......我們首先要做的是,我們附加一段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() { 
    //... 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();

一切正常, 腳本和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">Content here</div>

在DOM-Inspector中,如上所述,有console.log('hello world'); 測試它,但它沒有被執行。

編輯4

通過createDiv()函數為文檔的<head></head>添加了一些CSS,它會影響id為'just_id'的div。 JS仍然無法正常工作。

編輯5

還嘗試在我的函數中添加內聯javascript而不是外部javascript,但仍然沒有成功。 可以在DOM-Inspector中看到以下內容,但代碼不會記錄任何內容。

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

編輯6

我也嘗試從這里建議導入節點

不允許此方法在不同文檔之間移動節點。 如果要從其他文檔追加節點,則必須使用document.importNode()方法。

並試圖改變秩序。 1.css 2.div 3.js喜歡這個,但仍然沒有成功......任何人都知道這里發生了什么?

檢查上面的代碼

編輯7

您如何執行動態加載的JavaScript塊中所述? 我將div的innerHTML設置為scripts + content,然后像這樣附加到DOM:

檢查上面的代碼

但仍然沒有任何成功。

編輯8

添加了ajax功能代碼。 xmlhttp.open("GET","http://blabla.com/api/");第3個參數xmlhttp.open("GET","http://blabla.com/api/"); 已嘗試過true(異步)和false(同步)。 仍然沒有成功。

編輯9

函數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);

不行。 只有當我從其他文檔中獲取內聯腳本的innerHTML,創建回答中建議的元素,設置他們的innerHTML然后追加當前文檔時,才能工作。 外部腳本根本不會加載。

編輯10

來自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來自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

編輯11

所以我可能會給予賞金@Drakes,只是為了努力和他花在我的問題上的時間,並將顯示一些截圖。 它肯定是firefox bug,我剛剛測試了chrome並且它有效。

第1張圖片:源代碼192.168.2.197/testing/whatever/etc。 .htaccess與上面的示例相同

源代碼192.168.2.197

第二張圖片是script.js的源代碼,它被加載到192.168.2.197/testing/whatever/etc上。 .htaccess與上例中的相同:

Script.js來源

第3張圖是檢查員的截圖,我的dom看起來如何:

大教堂

第四張圖......沒有任何反應。 只是一個與我的問題無關的警告(我希望如此),因為即使我刪除所有腳本,它仍然會出現。

安慰

它是firefox的bug嗎?

問題是上面的代碼混合了appendChildinnerHTML 在某些情況下,兩者的表現不同,這在直覺上並不明顯。 前者允許新插入的腳本節點在附加到DOM時執行。 后者沒有。 對許多人來說,這一直是一個令人頭疼的問題。 請使用谷歌“腳本和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]; /* 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"); 

經測試

我在我的服務器上運行了OP代碼以盡可能地復制問題。 鑒於提供的代碼,我可以使它在我的服務器上按預期工作。 我只是將AJAX URL從http://blabla.com/api/更改為api.html並提供了我自己的文件,因為OP沒有提供,但是我將它應該包含在OP的JavaScript中。 以下是示例結果:

的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

這是根據我上面的規范修改的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 (由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

console.log("src script1");

以下是DevTools的結果,表明這是有效的。

開發工具檢查

控制台結果

頁面的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);
}; 

更新:您可能需要將腳本作為文本然后評估它)

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;
};

您當前正在將腳本加載到頁面中。 這是在頁面js已經執行之后發生的(因為它是異步的,並且通常加載外部資源比腳本執行花費的時間更長)。

所以...基本上你有正確的功能定義和所有東西,稍后從控制台調用它們應該工作(我想你已經嘗試過並且很好)。

仍然,對函數的調用,即使認為它們在那里也不會執行,因為加載后不會計算。

要解決此問題,您可以為具有腳本的頁面創建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();

問題是你的方法是異步而不是同步執行。 這意味着在插入動態腳本時頁面仍在加載。 有幾種方法可以這樣做:

  1. 最簡單的方法是使用jQuery $(document).append而不是document.createElement,如果你不介意使用庫。

  2. 您還可以使用jQuery $.getScript

  3. 如果您不想包含第3個庫,則必須考慮在插入腳本時延遲頁面加載的方法,例如創建提示窗口的alert會起作用,但用戶體驗不太好,想想更順暢方式。

  4. 使用XMLHTTPRequest檢索腳本。 這是同步的,我可以看到你熟悉它,因為你已經在腳本中使用它了。

祝你好運,編碼愉快。

您是否嘗試過使用jQuery的getScript()函數獲取src文件的URL並將其getScript()

更改您的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.
});

編輯

純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