DOMParser 将 <script> 标记附加到 <head>/<body> 但不执行

Posted

技术标签:

【中文标题】DOMParser 将 <script> 标记附加到 <head>/<body> 但不执行【英文标题】:DOMParser appending <script> tags to <head>/<body> but not executing 【发布时间】:2014-05-21 16:06:56 【问题描述】:

我正在尝试通过 DOMParser 将字符串解析为完整的 html 文档,然后用处理过的节点覆盖当前页面。该字符串包含完整的标记,包括&lt;!doctype&gt;&lt;html&gt;&lt;head&gt;&lt;body&gt; 节点。

// parse the string into a DOMDocument element:
var parser = new DOMParser();
var doc = parser.parseFromString(data, 'text/html');

// set the parsed head/body innerHTML contents into the current page's innerHTML
document.getElementsByTagName('head')[0].innerHTML = doc.getElementsByTagName('head')[0].innerHTML;
document.getElementsByTagName('body')[0].innerHTML = doc.getElementsByTagName('body')[0].innerHTML;

这样做的原因是它成功地获取了已解析的 HTML 节点并将它们呈现在页面上;但是,解析字符串内的&lt;head&gt;&lt;body&gt; 节点中存在的任何&lt;script&gt; 标记都无法执行=[。直接使用 html 标签(与 head / body 相反)进行测试会产生相同的结果。

我也尝试过使用.appendChild() 而不是.innerHTML(),但没有改变:

var elementHtml = document.getElementsByTagName('html')[0];

// remove the existing head/body nodes from the page
while (elementHtml.firstChild) elementHtml.removeChild(elementHtml.firstChild);

// append the parsed head/body tags to the existing html tag
elementHtml.appendChild(doc.getElementsByTagName('head')[0]);
elementHtml.appendChild(doc.getElementsByTagName('body')[0]);

有谁知道将字符串转换为完整 HTML 页面的方法执行其中包含的 javascript

如果有一个 DOMParser 的替代品可以提供相同的结果(例如覆盖整个文档),请随时推荐它/他们 =]

注意: 我使用它而不是document.write(data) 的更简单替代方案的原因是因为我需要在SSL 下IE 的postMessage() 回调中使用它; document.write()在IE中访问SSL页面时被post消息等回调事件阻塞=[

【问题讨论】:

你已经尝试过 jQuery 了吗?这里有函数$.parseHTML 用于解析HTML 文件。通过.append()添加脚本标签后也应该执行它... @tampis 是的,试过 jQuery;每当我将完整的 HTML 文档传递给parseHTML() 时,它似乎出于某种原因只解析了&lt;head&gt;;如果你有一个可行的例子,请务必让我看看(我很容易在测试中搞砸=P) 【参考方案1】:

你应该使用:

const sHtml = '<script>window.alert("Hello!")</script>';
const frag = document.createRange().createContextualFragment(sHtml)
document.body.appendChild( frag );

【讨论】:

【参考方案2】:

使用问题中描述的DOMParser() 将正确设置页面的&lt;head&gt;&lt;body&gt; 内容,但需要做更多工作才能让任何现有的&lt;script&gt; 标记执行。

这里的基本方法是在设置内容后拉出页面中所有&lt;script&gt;标签的列表,遍历该列表并动态创建一个new &lt;script&gt;标签现有内容的内容,然后将新内容添加到页面。

例子:

// create a DOMParser to parse the HTML content
var parser = new DOMParser();
var parsedDocument = parser.parseFromString(data, 'text/html');

// set the current page's <html> contents to the newly parsed <html> content
document.getElementsByTagName('html')[0].innerHTML = parsedDocument.getElementsByTagName('html')[0].innerHTML;

// get a list of all <script> tags in the new page
var tmpScripts = document.getElementsByTagName('script');
if (tmpScripts.length > 0) 
    // push all of the document's script tags into an array
    // (to prevent dom manipulation while iterating over dom nodes)
    var scripts = [];
    for (var i = 0; i < tmpScripts.length; i++) 
        scripts.push(tmpScripts[i]);
    

    // iterate over all script tags and create a duplicate tags for each
    for (var i = 0; i < scripts.length; i++) 
        var s = document.createElement('script');
        s.innerHTML = scripts[i].innerHTML;

        // add the new node to the page
        scripts[i].parentNode.appendChild(s);

        // remove the original (non-executing) node from the page
        scripts[i].parentNode.removeChild(scripts[i]);
    

【讨论】:

【参考方案3】:

这是 jQuery 1.8.3 (link to jsFiddle) 的工作演示:

var html = "<html><head><script>alert(42);</" + "script></head><body><h1>Hello World</h1></body></html>";

$(function () 
    html = $($.parseXML(html));

    $("head").append(html.find("script"));
    $("body").append(html.find("h1"));
);

因此,我使用了函数$.parseXML(),如果您的 HTML 也是有效的 XML,那么您显然只能使用它。不幸的是,相同的代码不适用于 jQuery 1.9.1(不再找到 &lt;script&gt; 标签):http://jsfiddle.net/6cECR/8/ 也许它是一个错误(或安全功能......)

【讨论】:

我需要追加&lt;head&gt;&lt;body&gt;标签的full内容;这种方法不起作用 - 如果我附加单个标签(如 &lt;h1&gt;&lt;script&gt;),它工作得很好,但是原始 javascript(即不是 jQuery)和单独附加 10k+ 嵌套标签效率不高足以认为这是一种有效的方法 =/ jsfiddle.net/6cECR/10 但它有问题...稍后我将再次讨论这个主题... 对不起,但是这个对我不起作用......旧版本的 jQuery 的依赖并不是一个很好的解决方案。仅基于此,我还没有测试它是否真的在我需要的场景中工作 - 在跨域回调函数中使用基于 SSL 的 Internet Explorer。

以上是关于DOMParser 将 <script> 标记附加到 <head>/<body> 但不执行的主要内容,如果未能解决你的问题,请参考以下文章

javascript,如何在将 DOMparser 与 text/html 一起使用时删除 <html><head><body> 元素

JavaScriptCore - 在 iOS 中访问 DOMParser

如何将string转成HtmlDocument

在 JavaScript 中不使用 DOMParser 的 XML 解析

用于大型 html 的 DOMParser

Grunt混淆代码报 XML DOMParser 错误