为什么 DOMParser 会改变空格?
Why does DOMParser alter whitespace?
为什么以下 DOMParser 的使用导致 HTML 与用作输入的 HTML 不同?它删除了 DOCTYPE 和顶级元素之间的空格,删除了文档元素和 head
之间的空格,并在 </body>
.
之前添加了一个换行符
我已经在 Google Chrome、Firefox 和 Safari 中对此进行了测试;我还 运行 与 JSoup 的类似代码,得到了完全相同的结果。所以我很确定这不是一个错误。我目前的理论是,这是由某处规范中的某种深奥的解析规则引起的。但可能还有其他我误解的事情。
const html = `<!DOCTYPE html>
<html>
<head>
<title>1</title>
</head>
<body>
<div>
Hello, World!
</div>
</body>
</html>`;
const setText = function(id,string) {
document.getElementById(id).appendChild(document.createTextNode(string));
};
const documentToString = function(d) {
return Array.prototype.slice.call(d.childNodes).map(function(node) {
if (node.nodeType == node.ELEMENT_NODE) return node.outerHTML;
if (node.nodeType == node.DOCUMENT_TYPE_NODE) return new XMLSerializer().serializeToString(node);
throw new TypeError("" + node);
}).join("");
};
setText("raw", html);
var parsed = new DOMParser().parseFromString(html,"text/html");
setText("parsed", parsed.documentElement.outerHTML);
setText("converted", documentToString(parsed));
setText("xmlserializer", new XMLSerializer().serializeToString(parsed));
#raw, #parsed, #converted, #xmlserializer { white-space: pre; font-family: monospace; }
h1 { font-size: 110%; font-weight: bold; font-family: sans-serif; }
<body>
<h1>Raw string</h1>
<div id="raw"></div>
<h1>Parsed top-level element</h1>
<div id="parsed"></div>
<h1>Using a document-to-string converter</h1>
<div id="converted"></div>
<h1>From XMLSerializer</h1>
<div id="xmlserializer"></div>
</body>
因为那是 the specs 要求做的。
HTML不是XML,会发生很多变换。例如,您可能没有意识到,您的 StackSnippet 包含一个重复的 <body>
标签,因为 HTML 部分实际上被片段的脚本包裹在这样的标签中。文档解析时忽略重复项。
console.log('how many bodies?', document.querySelectorAll('body').length);
<body><body><body></body></body></body>
类似的转换发生在 TextNode 上。
是的,这不是 DOMParser
的事情,它实际上是 HTML DOM 的解析,你在文档解析时有相同的行为:
frame.src = URL.createObjectURL(new Blob([
`<!DOCTYPE html>
<html>
<head>
<title>1</title>
</head>
<body>
<div>
Hello, World!
</div>
<script> parent.postMessage(document.documentElement.outerHTML, "*");
<\/script>
</body>
</html>`], {type: 'text/html'}));
onmessage = e => console.log(e.data);
<iframe id="frame"></iframe>
现在,如果您希望检索完全相同的字符串,则将其解析为 XML:
const html = `<!DOCTYPE html>
<html>
<head>
<title>1</title>
</head>
<body>
<div>
Hello, World!
</div>
</body>
</html>`;
const setText = function(id,string) {
document.getElementById(id).appendChild(document.createTextNode(string));
};
const documentToString = function(d) {
return Array.prototype.slice.call(d.childNodes).map(function(node) {
if (node.nodeType == node.ELEMENT_NODE) return node.outerHTML;
if (node.nodeType == node.DOCUMENT_TYPE_NODE) return new XMLSerializer().serializeToString(node);
throw new TypeError("" + node);
}).join("");
};
setText("raw", html);
var parsed = new DOMParser().parseFromString(html,"text/xml");
setText("parsed", parsed.documentElement.outerHTML);
setText("converted", documentToString(parsed));
setText("xmlserializer", new XMLSerializer().serializeToString(parsed));
#raw, #parsed, #converted, #xmlserializer { white-space: pre; font-family: monospace; }
h1 { font-size: 110%; font-weight: bold; font-family: sans-serif; }
<h1>Raw string</h1>
<div id="raw"></div>
<h1>Parsed top-level element</h1>
<div id="parsed"></div>
<h1>Using a document-to-string converter</h1>
<div id="converted"></div>
<h1>From XMLSerializer</h1>
<div id="xmlserializer"></div>
为什么以下 DOMParser 的使用导致 HTML 与用作输入的 HTML 不同?它删除了 DOCTYPE 和顶级元素之间的空格,删除了文档元素和 head
之间的空格,并在 </body>
.
我已经在 Google Chrome、Firefox 和 Safari 中对此进行了测试;我还 运行 与 JSoup 的类似代码,得到了完全相同的结果。所以我很确定这不是一个错误。我目前的理论是,这是由某处规范中的某种深奥的解析规则引起的。但可能还有其他我误解的事情。
const html = `<!DOCTYPE html>
<html>
<head>
<title>1</title>
</head>
<body>
<div>
Hello, World!
</div>
</body>
</html>`;
const setText = function(id,string) {
document.getElementById(id).appendChild(document.createTextNode(string));
};
const documentToString = function(d) {
return Array.prototype.slice.call(d.childNodes).map(function(node) {
if (node.nodeType == node.ELEMENT_NODE) return node.outerHTML;
if (node.nodeType == node.DOCUMENT_TYPE_NODE) return new XMLSerializer().serializeToString(node);
throw new TypeError("" + node);
}).join("");
};
setText("raw", html);
var parsed = new DOMParser().parseFromString(html,"text/html");
setText("parsed", parsed.documentElement.outerHTML);
setText("converted", documentToString(parsed));
setText("xmlserializer", new XMLSerializer().serializeToString(parsed));
#raw, #parsed, #converted, #xmlserializer { white-space: pre; font-family: monospace; }
h1 { font-size: 110%; font-weight: bold; font-family: sans-serif; }
<body>
<h1>Raw string</h1>
<div id="raw"></div>
<h1>Parsed top-level element</h1>
<div id="parsed"></div>
<h1>Using a document-to-string converter</h1>
<div id="converted"></div>
<h1>From XMLSerializer</h1>
<div id="xmlserializer"></div>
</body>
因为那是 the specs 要求做的。
HTML不是XML,会发生很多变换。例如,您可能没有意识到,您的 StackSnippet 包含一个重复的 <body>
标签,因为 HTML 部分实际上被片段的脚本包裹在这样的标签中。文档解析时忽略重复项。
console.log('how many bodies?', document.querySelectorAll('body').length);
<body><body><body></body></body></body>
类似的转换发生在 TextNode 上。
是的,这不是 DOMParser
的事情,它实际上是 HTML DOM 的解析,你在文档解析时有相同的行为:
frame.src = URL.createObjectURL(new Blob([
`<!DOCTYPE html>
<html>
<head>
<title>1</title>
</head>
<body>
<div>
Hello, World!
</div>
<script> parent.postMessage(document.documentElement.outerHTML, "*");
<\/script>
</body>
</html>`], {type: 'text/html'}));
onmessage = e => console.log(e.data);
<iframe id="frame"></iframe>
现在,如果您希望检索完全相同的字符串,则将其解析为 XML:
const html = `<!DOCTYPE html>
<html>
<head>
<title>1</title>
</head>
<body>
<div>
Hello, World!
</div>
</body>
</html>`;
const setText = function(id,string) {
document.getElementById(id).appendChild(document.createTextNode(string));
};
const documentToString = function(d) {
return Array.prototype.slice.call(d.childNodes).map(function(node) {
if (node.nodeType == node.ELEMENT_NODE) return node.outerHTML;
if (node.nodeType == node.DOCUMENT_TYPE_NODE) return new XMLSerializer().serializeToString(node);
throw new TypeError("" + node);
}).join("");
};
setText("raw", html);
var parsed = new DOMParser().parseFromString(html,"text/xml");
setText("parsed", parsed.documentElement.outerHTML);
setText("converted", documentToString(parsed));
setText("xmlserializer", new XMLSerializer().serializeToString(parsed));
#raw, #parsed, #converted, #xmlserializer { white-space: pre; font-family: monospace; }
h1 { font-size: 110%; font-weight: bold; font-family: sans-serif; }
<h1>Raw string</h1>
<div id="raw"></div>
<h1>Parsed top-level element</h1>
<div id="parsed"></div>
<h1>Using a document-to-string converter</h1>
<div id="converted"></div>
<h1>From XMLSerializer</h1>
<div id="xmlserializer"></div>