在JavaScript中严格的HTML解析

时间:2012-02-19 22:13:56

标签: javascript html html-parsing

在Google Chrome(Canary)上,似乎没有字符串可以使DOM解析器失败。我试图解析一些HTML,但如果HTML不完全,100%,有效,我希望它显示错误。我已经尝试过这个显而易见的事了:

var newElement = document.createElement('div');
newElement.innerHTML = someMarkup; // Might fail on IE, never on Chrome.

我也尝试了this question中的方法。对于无效标记,即使是我能生成的最无效的标记,也不会失败。

那么,有没有办法解析HTML"严格来说"至少在Google Chrome中?我不想自己使用令牌或使用外部验证实用程序。如果没有其他选择,那么严格的XML解析器就可以了,但某些元素不需要在HTML中关闭标签,最好不要那些失败。

1 个答案:

答案 0 :(得分:6)

使用DOMParser分两步检查文档:

  1. 通过将文档解析为XML来验证文档是否符合XML。
  2. 将字符串解析为HTML。这需要modification on the DOMParser
    遍历每个元素,并检查DOM元素是否是HTMLUnknownElement的实例。为此,getElementsByTagName('*')非常适合 (如果要严格解析文档,则必须递归遍历每个元素,并记住元素在该位置是否为allowed to be placed。例如<area>中的<map>
  3. 演示:http://jsfiddle.net/q66Ep/1/

    /* DOM parser for text/html, see https://stackoverflow.com/a/9251106/938089 */
    ;(function(DOMParser) {"use strict";var DOMParser_proto=DOMParser.prototype,real_parseFromString=DOMParser_proto.parseFromString;try{if((new DOMParser).parseFromString("", "text/html"))return;}catch(e){}DOMParser_proto.parseFromString=function(markup,type){if(/^\s*text\/html\s*(;|$)/i.test(type)){var doc=document.implementation.createHTMLDocument(""),doc_elt=doc.documentElement,first_elt;doc_elt.innerHTML=markup;first_elt=doc_elt.firstElementChild;if (doc_elt.childElementCount===1&&first_elt.localName.toLowerCase()==="html")doc.replaceChild(first_elt,doc_elt);return doc;}else{return real_parseFromString.apply(this, arguments);}};}(DOMParser));
    
    /*
     * @description              Validate a HTML string
     * @param       String html  The HTML string to be validated 
     * @returns            null  If the string is not wellformed XML
     *                    false  If the string contains an unknown element
     *                     true  If the string satisfies both conditions
     */
    function validateHTML(html) {
        var parser = new DOMParser()
          , d = parser.parseFromString('<?xml version="1.0"?>'+html,'text/xml')
          , allnodes;
        if (d.querySelector('parsererror')) {
            console.log('Not welformed HTML (XML)!');
            return null;
        } else {
            /* To use text/html, see https://stackoverflow.com/a/9251106/938089 */
            d = parser.parseFromString(html, 'text/html');
            allnodes = d.getElementsByTagName('*');
            for (var i=allnodes.length-1; i>=0; i--) {
                if (allnodes[i] instanceof HTMLUnknownElement) return false;
            }
        }
        return true; /* The document is syntactically correct, all tags are closed */
    }
    
    console.log(validateHTML('<div>'));  //  null, because of the missing close tag
    console.log(validateHTML('<x></x>'));// false, because it's not a HTML element
    console.log(validateHTML('<a></a>'));//  true, because the tag is closed,
                                         //       and the element is a HTML element
    

    有关没有DOMParser的XML验证的替代方法,请参阅revision 1 of this answer

    考虑

    • 当前方法完全忽略doctype,以进行验证。
    • 此方法会为null返回<input type="text">,而它是有效的HTML5(因为标记未关闭)。
    • 未检查一致性。