2017-02-10 77 views
1

測試頁:https://jsfiddle.net/y25rk55w/不能腳本加載到iframe中

在這個測試頁面,您可以看到內嵌到對方3 的。每個包含一個<script>標記,其標記爲<head>

問題是:只有<script>中的第一個將被瀏覽器加載。另外兩個<script>標籤將出現在dom中,但瀏覽器永遠不會嘗試加載它們。問題不是瀏覽器特定的,它可以在Chrome,Firefox中重新生成。通過添加超時或在追加腳本之前等待,無法解決問題。所有iframe都有編程式生成的內容似乎很重要;如果用帶實際src鏈接的iframe替換此iframe,問題將消失。

問題是:我怎麼能實際加載腳本到iframes 2和3?

完整的測試代碼:

// It doesn't matter if the scripts exist or not 
// Browser won't try to load them either way 
var scripts = [ 
    '//testdomain.test/script1.js', 
    '//testdomain.test/script2.js', 
    '//testdomain.test/script3.js' 
]; 

function createIFrame(win, onCreated) { 
    var iframe = win.document.createElement('iframe'); 
    iframe.onload = function() { 
     onCreated(iframe); 
    }; 
    win.document.body.appendChild(iframe); 
} 

function loadScript(win, url) { 
    var script = win.document.createElement('script'); 
    script.src = url; 
    script.onload = function() { 
     console.log("Script " + url + " is loaded."); 
    }; 
    win.document.getElementsByTagName('head')[0].appendChild(script); 
} 

createIFrame(window, function(iframe1) { 
    loadScript(iframe1.contentWindow, scripts[0]); 
    createIFrame(iframe1.contentWindow, function (iframe2) { 
     loadScript(iframe2.contentWindow, scripts[1]); 
     createIFrame(iframe2.contentWindow, function (iframe3) { 
      loadScript(iframe3.contentWindow, scripts[2]); 
     }); 
    }); 
}); 
+0

您在控制檯中遇到什麼錯誤? – putvande

回答

1

在這個問題你可以看到,我是ommiting協議:

/* This is valid to omit the http:/https: protocol. 
    In that case, browser should automatically append 
    protocol used by the parent page */ 
var scripts = [ 
    '//testdomain.test/script1.js', 
    '//testdomain.test/script2.js', 
    '//testdomain.test/script3.js' 
]; 

的事情是,編程創建的I幀有協議about:(或javascript:,這取決於你如何創建它們)。我仍然無法解釋爲什麼第一個腳本正在加載,或者爲什麼其他兩個腳本根本沒有顯示在網絡標籤中,但我想這不是很重要。

解決辦法:要麼明確使用https://或編程方式使用類似下面的代碼附加協議:

function appendSchema(win, url) { 
    if (url.startsWith('//')) { 
     var protocol = 'https:'; 
     try { 
      var wPrev = undefined; 
      var wCur = win; 
      while (wPrev != wCur) { 
       console.log(wCur.location.protocol); 
       if (wCur.location.protocol.startsWith("http")) { 
        protocol = wCur.location.protocol; 
        break; 
       } 
       wPrev = wCur; 
       wCur = wCur.parent; 
      } 
     } catch (e) { 
      /* We cannot get protocol of a cross-site iframe. 
      * So in case we are inside cross-site iframe, and 
      * there are no http/https iframes before it, 
      * we will just use https: */ 
     } 
     return protocol + url; 
    } 
    return url; 
} 
1

您的代碼工作正常 - >http://plnkr.co/edit/vQGsyD7JxZiDlg6EZvK4?p=preview

確保您在window.onloadDOMContentLoaded執行createIFrame。

var scripts = [ 
    'https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.1/jquery.js', 
    'https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.2/jquery.js', 
    'https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.3/jquery.js' 
]; 

function createIFrame(win, onCreated) { 
    var iframe = win.document.createElement('iframe'); 
    iframe.onload = function() { 
     onCreated(iframe); 
    }; 
    win.document.body.appendChild(iframe); 
} 

function loadScript(win, url) { 
    var script = win.document.createElement('script'); 
    script.src = url; 
    script.onload = function() { 
     console.log("Script " + url + " is loaded."); 
    }; 
    win.document.getElementsByTagName('head')[0].appendChild(script); 
} 
window.onload = function(){ 
createIFrame(window, function(iframe1) { 
    loadScript(iframe1.contentWindow, scripts[0]); 
    createIFrame(iframe1.contentWindow, function (iframe2) { 
     loadScript(iframe2.contentWindow, scripts[1]); 
     createIFrame(iframe2.contentWindow, function (iframe3) { 
      loadScript(iframe3.contentWindow, scripts[2]); 
     }); 
    }); 
}); 
}; 
+1

謝謝。即使你的回答沒有解決我的問題,但它提供瞭解決問題的重要線索(請參閱我的答案)。 – Alexey

0

我已經成功地使用比什麼OP在self-answer提出了更簡單的方法。

new URL(scriptURL, window.location.href).toString(); 

其中scriptURL是需要被固定以獲得適當的協議,window是保存腳本iframe元素的的網址:我使用產生的URL。這可以處理與OP示例URL不同的場景:例如相對URL(../foo.js)或絕對URL不以主機開頭(/foo.js)。上面的代碼對我來說已經足夠了。

如果我要通過OP所使用的窗口層次來複制搜索,我可能會做類似以下的事情。這是TypeScript代碼。刪除類型註釋以獲得普通的JavaScript。

function url(win: Window, path: string): string { 
    // We search up the window hierarchy for the first window which uses 
    // a protocol that starts with "http". 
    while (true) { 
    if (win.location.protocol.startsWith("http")) { 
     // Interpret the path relative to that window's href. So the path 
     // will acquire the protocol used by the window. And the less we 
     // specify in `path`, the more it gets from the window. For 
     // instance, if path is "/foo.js", then the host name will also be 
     // acquired from the window's location. 
     return new URL(path, win.location.href).toString(); 
    } 

    // We searched all the way to the top and found nothing useful. 
    if (win === win.parent) { 
     break; 
    } 

    win = win.parent; 
    } 

    // I've got a big problem on my hands if there's nothing that works. 
    throw new Error("cannot normalize the URL"); 
} 

我沒有默認的返回值,如果窗口鏈產量空話有用的,因爲這將表明一個比生產的URL的問題更大的問題。在我的設置中其他地方會出現問題。