检查HTML代码段是否对Javascript有效

Ixx*_*Ixx 30 html javascript validation

我需要一个可靠的Javascript库/函数来检查HTML代码段是否有效,我可以从我的代码中调用它.例如,它应检查打开的标签和引号是否已关闭,嵌套是否正确等.

我不希望验证失败,因为某些东西不是100%标准(但无论如何都会起作用).

mik*_*ana 33

更新:此答案有限 - 请参阅下面的编辑.

扩展@kolink的答案,我使用:

var checkHTML = function(html) {
  var doc = document.createElement('div');
  doc.innerHTML = html;
  return ( doc.innerHTML === html );
}
Run Code Online (Sandbox Code Playgroud)

也就是说,我们用HTML创建一个临时div.为此,浏览器将根据HTML字符串创建DOM树,这可能涉及关闭标签等.

将div的HTML内容与原始HTML进行比较将告诉我们浏览器是否需要更改任何内容.

checkHTML('<a>hell<b>o</b>')
Run Code Online (Sandbox Code Playgroud)

返回false.

checkHTML('<a>hell<b>o</b></a>')
Run Code Online (Sandbox Code Playgroud)

返回true.

编辑:正如下面的@Quentin所说,由于各种原因,这是非常严格的:浏览器通常会修复省略的结束标记,即使结束标记对于该标记是可选的.例如:

<p>one para
<p>second para
Run Code Online (Sandbox Code Playgroud)

...被认为是有效的(因为允许Ps省略结束标记),但checkHTML将返回false.浏览器还会标记标签案例,并改变空白区域.在决定使用此方法时,您应该了解这些限制.

  • 即使`<P>`是`</ p>`,它仍然是有效的HTML.您可以在div中包含文本节点.这只是一个例子.它将更改属性值周围使用的引号.小写属性和元素名称.改变白色空间.它会产生*批次*误报. (2认同)

Nie*_*sol 18

好吧,这段代码:

function tidy(html) {
    var d = document.createElement('div');
    d.innerHTML = html;
    return d.innerHTML;
}
Run Code Online (Sandbox Code Playgroud)

这将"纠正"格式错误的HTML到浏览器的最佳能力.如果这对您有帮助,那么比尝试验证HTML要容易得多.


all*_*kim 10

9 年后,使用DOMParser怎么样?

它接受字符串作为参数并返回文档类型,就像 HTML 一样。因此,当出现错误时,返回的文档对象<parsererror>中包含元素。

如果您将 html 解析为 xml,至少您可以检查您的 html 是否符合 xhtml。

例子

> const parser = new DOMParser();
> const doc = parser.parseFromString('<div>Input: <input /></div>', 'text/xml');
> (doc.documentElement.querySelector('parsererror') || {}).innerText; // undefined
Run Code Online (Sandbox Code Playgroud)

将其包装为函数

function isValidHTML(html) {
  const parser = new DOMParser();
  const doc = parser.parseFromString(html, 'text/xml');
  if (doc.documentElement.querySelector('parsererror')) {
    return doc.documentElement.querySelector('parsererror').innerText;
  } else {
    return true;
  }
}
Run Code Online (Sandbox Code Playgroud)

测试上述功能

isValidHTML('<a>hell<B>o</B></a>') // true
isValidHTML('<a href="test.html">hell</a>') // true
isValidHTML('<a href='test.html'>hell</a>') // true
isValidHTML("<a href=test.html>hell</a>")  // This page contains the following err..
isValidHTML('<ul><li>a</li><li>b</li></ul>') // true
isValidHTML('<ul><li>a<li>b</ul>') // This page contains the following err..
isValidHTML('<div><input /></div>' // true
isValidHTML('<div><input></div>' // This page contains the following err..
Run Code Online (Sandbox Code Playgroud)

上面的代码适用于非常简单的 html。但是,如果您的 html 有一些类似代码的文本;<script><style>等,尽管它是有效的 HTML,但您只需要进行操作以进行 XML 验证

以下代码将类似 html 的代码更新为有效的 XML 语法。

export function getHtmlError(html) {
  const parser = new DOMParser();
  const htmlForParser = `<xml>${html}</xml>`
    .replace(/(src|href)=".*?&.*?"/g, '$1="OMITTED"')
    .replace(/<script[\s\S]+?<\/script>/gm, '<script>OMITTED</script>')
    .replace(/<style[\s\S]+?<\/style>/gm, '<style>OMITTED</style>')
    .replace(/<pre[\s\S]+?<\/pre>/gm, '<pre>OMITTED</pre>')
    .replace(/&nbsp;/g, '&#160;');

  const doc = parser.parseFromString(htmlForParser, 'text/xml');
  if (doc.documentElement.querySelector('parsererror')) {
    console.error(htmlForParser.split(/\n/).map( (el, ndx) => `${ndx+1}: ${el}`).join('\n'));
    return doc.documentElement.querySelector('parsererror');
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 看来 isValidHTML() 函数在 Firefox 中无法正常工作...即使 Firefox 在通过无效的 HTML 测试(例如 isValidHTML('&lt;html&gt;&lt;b&gt;test&lt;/html&gt;'); 时会向控制台抛出 XML 错误);该函数仍然落入 else 块并始终返回 TRUE。我认为它在 Chrome 中仍然有效。除了“doc.documentElement.querySelector('parsererror')”之外,还有其他方法可以检查吗? (2认同)

foo*_*red 8

到目前为止提出的解决方案都不能很好地回答原始问题,尤其是在

我不希望验证失败,因为某些标准不是100%标准的(但是仍然可以使用)。

tldr >>检查JSFiddle

因此,我使用了有关该主题的答案和评论的输入,并创建了一种执行以下操作的方法:

  • 检查HTML字符串标签是否有效
  • 尝试呈现html字符串
  • 理论上比较要创建的标签数与实际呈现的html dom标签数
  • 如果选中“严格”,则不忽略<br/>空属性规范化=""
  • 比较渲染的innerHTML与给定的html字符串(而忽略空格和引号)

退货

  • 如果呈现的html与给定的html字符串相同,则为true
  • 如果其中一项检查失败,则返回false
  • 标准化的html字符串(如果呈现的html似乎有效,但不等于给定的html字符串)

标准化是指,在呈现时,浏览器有时会忽略或修复输入的特定部分(例如为输入添加缺少的结束标记<p>并转换其他部分(例如单引号,双引号或与号的编码)。在“失败”和“标准化”允许将内容标记给用户,因为“不会像您期望的那样呈现该内容”。

多数情况下,规范化只返回原始html字符串的稍有变化的版本-有时,结果却大不相同。因此,在将其保存到数据库或盲目渲染之前,应使用它来标记用户输入以供进一步检查。(有关标准化示例,请参见JSFiddle

检查将以下例外情况考虑在内

  • 忽略将单引号标准化为双引号
  • image和其他带有src属性的标签在渲染过程中被“撤防”
  • (如果不严格)忽略<br/>>> <br>转换
  • (如果不严格)忽略空属性的规范化(<p disabled>>> <p disabled="">
  • 读取时最初未编码的“&”号的编码.innerHTML,例如在属性值中

function simpleValidateHtmlStr(htmlStr, strictBoolean) {
  if (typeof htmlStr !== "string")
    return false;

  var validateHtmlTag = new RegExp("<[a-z]+(\s+|\"[^\"]*\"\s?|'[^']*'\s?|[^'\">])*>", "igm"),
    sdom = document.createElement('div'),
    noSrcNoAmpHtmlStr = htmlStr
      .replace(/ src=/, " svhs___src=") // disarm src attributes
      .replace(/&amp;/igm, "#svhs#amp##"), // 'save' encoded ampersands
    noSrcNoAmpIgnoreScriptContentHtmlStr = noSrcNoAmpHtmlStr
      .replace(/\n\r?/igm, "#svhs#nl##") // temporarily remove line breaks
      .replace(/(<script[^>]*>)(.*?)(<\/script>)/igm, "$1$3") // ignore script contents
      .replace(/#svhs#nl##/igm, "\n\r"),  // re-add line breaks
    htmlTags = noSrcNoAmpIgnoreScriptContentHtmlStr.match(/<[a-z]+[^>]*>/igm), // get all start-tags
    htmlTagsCount = htmlTags ? htmlTags.length : 0,
    tagsAreValid, resHtmlStr;


  if(!strictBoolean){
    // ignore <br/> conversions
    noSrcNoAmpHtmlStr = noSrcNoAmpHtmlStr.replace(/<br\s*\/>/, "<br>")
  }

  if (htmlTagsCount) {
    tagsAreValid = htmlTags.reduce(function(isValid, tagStr) {
      return isValid && tagStr.match(validateHtmlTag);
    }, true);

    if (!tagsAreValid) {
      return false;
    }
  }


  try {
    sdom.innerHTML = noSrcNoAmpHtmlStr;
  } catch (err) {
    return false;
  }

  // compare rendered tag-count with expected tag-count
  if (sdom.querySelectorAll("*").length !== htmlTagsCount) {
    return false;
  }

  resHtmlStr = sdom.innerHTML.replace(/&amp;/igm, "&"); // undo '&' encoding

  if(!strictBoolean){
    // ignore empty attribute normalizations
    resHtmlStr = resHtmlStr.replace(/=""/, "")
  }

  // compare html strings while ignoring case, quote-changes, trailing spaces
  var
    simpleIn = noSrcNoAmpHtmlStr.replace(/["']/igm, "").replace(/\s+/igm, " ").toLowerCase().trim(),
    simpleOut = resHtmlStr.replace(/["']/igm, "").replace(/\s+/igm, " ").toLowerCase().trim();
  if (simpleIn === simpleOut)
    return true;

  return resHtmlStr.replace(/ svhs___src=/igm, " src=").replace(/#svhs#amp##/, "&amp;");
}
Run Code Online (Sandbox Code Playgroud)

在这里,您可以在JSFiddle https://jsfiddle.net/abernh/twgj8bev/中找到它,以及不同的测试用例,包括

"<a href='blue.html id='green'>missing attribute quotes</a>" // FAIL
"<a>hell<B>o</B></a>"                                        // PASS
'<a href="test.html">hell<b>o</b></a>'                       // PASS
'<a href=test.html>hell<b>o</b></a>',                        // PASS
"<a href='test.html'>hell<b>o</b></a>",                      // PASS
'<ul><li>hell</li><li>hell</li></ul>',                       // PASS
'<ul><li>hell<li>hell</ul>',                                 // PASS
'<div ng-if="true && valid">ampersands in attributes</div>'  // PASS
Run Code Online (Sandbox Code Playgroud)