2013-06-29 14 views
0

我想在頁面加載時替換整個DOM,爲用戶創建的淘汰頁面執行no-js後備。用JavaScript代替DOM並運行新腳本

我讓它替換DOM,但是當我做新腳本中包含的腳本沒有運行。我想知道是否有任何方法強迫他們跑。

<!DOCTYPE html> 
<html> 
    <head> 
     <title>Title1</title> 
    </head> 
    <body> 
     Hello world <!-- No JS enabled content --> 
    </body> 
    <script type="text/javascript"> 
     var model = { 'template' : '\u003chtml\u003e\u003chead\u003e\u003ctitle\u003eTitle2\u003c/title\u003e\u003cscript type=\"text/javascript\"\u003ealert(\"test\");\u003c/script\u003e\u003c/head\u003e\u003cbody\u003eHello world2\u003c/body\u003e\u003c/html\u003e' }; 
     document.documentElement.innerHTML = model.template; 
    </script> 
</html> 

模板包含以下編碼

<html> 
    <head> 
     <title>aaa</title> 
     <script type='text/javascript'>alert('hello world');</script> 
    </head> 
    <body> 
     Hello world <!-- JS enabled content --> 
    </body> 
</html> 

我怎麼能得到警報運行?

+4

如果這是一個沒有JS後備我假設你測試它沒有啓用JavaScript,所以沒有運行的腳本只是邏輯..或我在這裏丟失了什麼? –

+1

類似的問題在這裏:http://stackoverflow.com/q/1197575/575527 – Joseph

+0

@BenjaminGruenbaum這是運行與JS啓用,DOM正確替換警報腳本不運行,另一個。 –

回答

3

正如您發現的,您分配給innerHTML的文本中script標記中的代碼未被執行。有趣的是,儘管在我嘗試過的每個瀏覽器上,script元素都是創建並放置在DOM中。

這意味着它很容易寫來運行它們的功能,從而,沒有對使用範圍和eval其怪異的效果:

function runScripts(element) { 
    var scripts; 

    // Get the scripts 
    scripts = element.getElementsByTagName("script"); 

    // Run them in sequence (remember NodeLists are live) 
    continueLoading(); 

    function continueLoading() { 
    var script, newscript; 

    // While we have a script to load... 
    while (scripts.length) { 
     // Get it and remove it from the DOM 
     script = scripts[0]; 
     script.parentNode.removeChild(script); 

     // Create a replacement for it 
     newscript = document.createElement('script'); 

     // External? 
     if (script.src) { 
     // Yes, we'll have to wait until it's loaded before continuing 
     newscript.onerror = continueLoadingOnError; 
     newscript.onload = continueLoadingOnLoad; 
     newscript.onreadystatechange = continueLoadingOnReady; 
     newscript.src = script.src; 
     } 
     else { 
     // No, we can do it right away 
     newscript.text = script.text; 
     } 

     // Start the script 
     document.documentElement.appendChild(newscript); 

     // If it's external, wait for callback 
     if (script.src) { 
     return; 
     } 
    } 

    // All scripts loaded 
    newscript = undefined; 

    // Callback on most browsers when a script is loaded 
    function continueLoadingOnLoad() { 
     // Defend against duplicate calls 
     if (this === newscript) { 
     continueLoading(); 
     } 
    } 

    // Callback on most browsers when a script fails to load 
    function continueLoadingOnError() { 
     // Defend against duplicate calls 
     if (this === newscript) { 
     continueLoading(); 
     } 
    } 

    // Callback on IE when a script's loading status changes 
    function continueLoadingOnReady() { 

     // Defend against duplicate calls and check whether the 
     // script is complete (complete = loaded or error) 
     if (this === newscript && this.readyState === "complete") { 
     continueLoading(); 
     } 
    } 
    } 
} 

自然的腳本不能使用document.write

注意我們如何創建一個新的script元素。僅僅移動文檔中其他位置的現有文檔不起作用,瀏覽器將其標記爲已經運行(儘管不是)。

上述內容適用於大多數人在文檔正文的某個元素上使用innerHTML,但它不適用於您,因爲您實際上是在document.documentElement上執行此操作。這意味着NodeList我們從該行得到的結果:

// Get the scripts 
scripts = element.getElementsByTagName("script"); 

...將不斷擴大,我們的document.documentElement添加更多的腳本。因此,在您的特定情況下,你必須首先把它變成一個數組:

var list, scripts, index; 

// Get the scripts 
list = element.getElementsByTagName("script"); 
scripts = []; 
for (index = 0; index < list.length; ++index) { 
    scripts[index] = list[index]; 
} 
list = undefined; 

...後來在continueLoading,你必須手動從陣列中刪除條目:

// Get it and remove it from the DOM 
script = scripts[0]; 
script.parentNode.removeChild(script); 
scripts.splice(0, 1); // <== The new line 

這裏有一個大多數人(不是你)的完整例子,包括像函數聲明一樣的腳本(如果我們使用eval,將會搞砸):Live Copy | Live Source

<!DOCTYPE html> 
<html> 
<head> 
<meta charset=utf-8 /> 
<title>Run Scripts</title> 
</head> 
<body> 
    <div id="target">Click me</div> 
    <script> 
    document.getElementById("target").onclick = function() { 
     display("Updating div"); 
     this.innerHTML = 
     "Updated with script" + 
     "<div id='sub'>sub-div</div>" + 
     "<script src='http://ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js'></scr" + "ipt>" + 
     "<script>" + 
     "display('Script one run');" + 
     "function foo(msg) {" + 
     " display(msg); " + 
     "}" + 
     "</scr" + "ipt>" + 
     "<script>" + 
     "display('Script two run');" + 
     "foo('Function declared in script one successfully called from script two');" + 
     "$('#sub').html('updated via jquery');" + 
     "</scr" + "ipt>"; 
     runScripts(this); 
    }; 
    function runScripts(element) { 
     var scripts; 

     // Get the scripts 
     scripts = element.getElementsByTagName("script"); 

     // Run them in sequence (remember NodeLists are live) 
     continueLoading(); 

     function continueLoading() { 
     var script, newscript; 

     // While we have a script to load... 
     while (scripts.length) { 
      // Get it and remove it from the DOM 
      script = scripts[0]; 
      script.parentNode.removeChild(script); 

      // Create a replacement for it 
      newscript = document.createElement('script'); 

      // External? 
      if (script.src) { 
      // Yes, we'll have to wait until it's loaded before continuing 
      display("Loading " + script.src + "..."); 
      newscript.onerror = continueLoadingOnError; 
      newscript.onload = continueLoadingOnLoad; 
      newscript.onreadystatechange = continueLoadingOnReady; 
      newscript.src = script.src; 
      } 
      else { 
      // No, we can do it right away 
      display("Loading inline script..."); 
      newscript.text = script.text; 
      } 

      // Start the script 
      document.documentElement.appendChild(newscript); 

      // If it's external, wait for callback 
      if (script.src) { 
      return; 
      } 
     } 

     // All scripts loaded 
     newscript = undefined; 

     // Callback on most browsers when a script is loaded 
     function continueLoadingOnLoad() { 
      // Defend against duplicate calls 
      if (this === newscript) { 
      display("Load complete, next script"); 
      continueLoading(); 
      } 
     } 

     // Callback on most browsers when a script fails to load 
     function continueLoadingOnError() { 
      // Defend against duplicate calls 
      if (this === newscript) { 
      display("Load error, next script"); 
      continueLoading(); 
      } 
     } 

     // Callback on IE when a script's loading status changes 
     function continueLoadingOnReady() { 

      // Defend against duplicate calls and check whether the 
      // script is complete (complete = loaded or error) 
      if (this === newscript && this.readyState === "complete") { 
      display("Load ready state is complete, next script"); 
      continueLoading(); 
      } 
     } 
     } 
    } 
    function display(msg) { 
     var p = document.createElement('p'); 
     p.innerHTML = String(msg); 
     document.body.appendChild(p); 
    } 
    </script> 
</body> 
</html> 

而這裏的your fiddle updated to use the above我們打開NodeList到一個數組:

HTML:

<body> 
    Hello world22 
</body> 

腳本:

var model = { 
    'template': '\t\u003chtml\u003e\r\n\t\t\u003chead\u003e\r\n\t\t\t\u003ctitle\u003eaaa\u003c/title\u003e\r\n\t\t\t\u003cscript src=\"http://cdnjs.cloudflare.com/ajax/libs/jquery/1.10.1/jquery.min.js\"\u003e\u003c/script\u003e\r\n\t\t\t\u003cscript type=\u0027text/javascript\u0027\u003ealert($(\u0027body\u0027).html());\u003c/script\u003e\r\n\t\t\u003c/head\u003e\r\n\t\t\u003cbody\u003e\r\n\t\t\tHello world\r\n\t\t\u003c/body\u003e\r\n\t\u003c/html\u003e' 
}; 
document.documentElement.innerHTML = model.template; 

function runScripts(element) { 
    var list, scripts, index; 

    // Get the scripts 
    list = element.getElementsByTagName("script"); 
    scripts = []; 
    for (index = 0; index < list.length; ++index) { 
     scripts[index] = list[index]; 
    } 
    list = undefined; 

    // Run them in sequence 
    continueLoading(); 

    function continueLoading() { 
     var script, newscript; 

     // While we have a script to load... 
     while (scripts.length) { 
      // Get it and remove it from the DOM 
      script = scripts[0]; 
      script.parentNode.removeChild(script); 
      scripts.splice(0, 1); 

      // Create a replacement for it 
      newscript = document.createElement('script'); 

      // External? 
      if (script.src) { 
       // Yes, we'll have to wait until it's loaded before continuing 
       newscript.onerror = continueLoadingOnError; 
       newscript.onload = continueLoadingOnLoad; 
       newscript.onreadystatechange = continueLoadingOnReady; 
       newscript.src = script.src; 
      } else { 
       // No, we can do it right away 
       newscript.text = script.text; 
      } 

      // Start the script 
      document.documentElement.appendChild(newscript); 

      // If it's external, wait 
      if (script.src) { 
       return; 
      } 
     } 

     // All scripts loaded 
     newscript = undefined; 

     // Callback on most browsers when a script is loaded 

     function continueLoadingOnLoad() { 
      // Defend against duplicate calls 
      if (this === newscript) { 
       continueLoading(); 
      } 
     } 

     // Callback on most browsers when a script fails to load 

     function continueLoadingOnError() { 
      // Defend against duplicate calls 
      if (this === newscript) { 
       continueLoading(); 
      } 
     } 

     // Callback on IE when a script's loading status changes 

     function continueLoadingOnReady() { 

      // Defend against duplicate calls and check whether the 
      // script is complete (complete = loaded or error) 
      if (this === newscript && this.readyState === "complete") { 
       continueLoading(); 
      } 
     } 
    } 
} 
runScripts(document.documentElement); 

這種方法當今天剛剛發生在我身上閱讀你的問題。我以前從未見過它,但它可以在IE6,IE8,Chrome 26,Firefox 20和Opera 12.15中使用。

+0

嘿,這看起來非常好,讓我的上面的例子完美的工作,所以即將標記爲答案。不過,如果我有一個外部腳本,例如在它之後執行的腳本之後加載的jquery引用,我確實遇到了使用此方法排序的問題。有什麼辦法可以避免這種情況?繼承人一個JSFiddle來演示我的意思http://jsfiddle.net/6YwnK/,看看錯誤控制檯 –

+0

@LukeMcGregor:問題不在於訂單已關閉,而是因爲該功能完全沒有關閉, t迎合外部腳本。我不知道什麼進入了我。我已經更新了,並且還處理了一個獨特的問題,這個問題不會影響到大多數其他人。 –

+0

是的,當我第一次嘗試時,我添加了src和類型位,它清理了加載問題,我錯過了繼續加載位來強制連續加載。我將其添加到我的代碼中,謝謝:) –