【问题标题】:Why does DOMParser alter whitespace?为什么 DOMParser 会改变空格?
【发布时间】:2019-08-07 12:42:39
【问题描述】:

为什么下面使用 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>

【问题讨论】:

  • 标签之间的空格在语法上并不重要。一百万个空格等于一个空格。
  • “但在很多情况下,人们希望保留格式” - 那么,就必须在文本级别而不是 DOM 上进行操作级别:-)
  • XML DOM 解析器通常准确地保留空白,作为其他节点之间的纯空白文本节点。解析文档然后对其进行序列化将返回相同的字符串。无论如何,为什么解析器插入空格(在文档正文的末尾)?
  • 而且,对于那些说“空白无关紧要”的人来说,解析器并不是要删除空白。它主要是保留它,但是在两个特定的地方,每个实现都在同一个地方删除一个纯空格文本节点,每个实现都在同一个地方插入一个纯空格文本节点。
  • 我想知道这有什么问题,我的意思是你已经在代码“exhibit a”中向我们展示了你的意思,关于 dom 的序列化看起来与一个人类读者,但我想知道问题是什么。

标签: javascript html domparser


【解决方案1】:

因为那是the specs 要求做的事情。

HTML 不是 XML,会发生很多转换。例如,您可能没有意识到,但您的 StackSnippet 包含一个重复的 &lt;body&gt; 标签,因为 HTML 部分实际上是由 sn-p 的脚本包装在这样的标签中的。文档解析时忽略重复项。

console.log('how many bodies?', document.querySelectorAll('body').length);
&lt;body&gt;&lt;body&gt;&lt;body&gt;&lt;/body&gt;&lt;/body&gt;&lt;/body&gt;

TextNodes 也发生了类似的转变。

是的,这不是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);
&lt;iframe id="frame"&gt;&lt;/iframe&gt;

现在,如果您希望检索完全相同的字符串,请将其解析为 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>

【讨论】:

  • 许多有效的 HTML 文档不是有效的(或格式正确的)XML,所以这不是一个真正的选择。我很欣赏该规范的链接,但我不清楚该规范中的哪些解析规则会导致 1. 删除 DOCTYPE 和 html 之间的空格,2. 删除 html 和 head 之间的空格,3. 在正文的结尾,4. 在每个实现中保留所有其他空格。必须有规则,否则它们将不会以相同的高度具体的方式表现。
  • 很抱歉,我无法浏览所有引用相关规范的文档 ;-) 对于 之一,it's here,第一种情况“U +0020 SPACE 忽略角色。”另外,我不明白你关于 html 不是有效的 XML 的观点。当然不是,这就是为什么 HTML 在解析规则上比 XML 更宽松的原因。但是,这也意味着您拥有的 DOM 不是您提供的标记,并且您无法从 DOM 中检索此标记。
  • 对不起,我的 !DOCTYPE>At tokenization,&lt;!DOCTYPE&gt; 末尾的 &gt; 将我们发送到刚刚发出空格字符的 data state。在插入时它将被忽略,在添加文档类型后,我们输入before-html-insertion-mode,这里第三个项目符号告诉我们忽略该空格字符标记。
猜你喜欢
  • 2016-06-24
  • 1970-01-01
  • 2015-11-26
  • 2021-04-28
  • 2013-03-23
  • 1970-01-01
  • 2010-12-18
  • 2011-01-15
相关资源
最近更新 更多