O XML tem uma palavra para marcação que ele não lê: não bem formado, pare, mostre um erro. O HTML não tem palavra assim. Um navegador que recebe <p>a<b>b<i>c</b>d</i> não reclama; constrói uma árvore, pinta-a e segue, e o autor da página pode nunca saber que a árvore não é a que escreveu. Por muito tempo cada navegador construía uma árvore diferente da mesma marcação quebrada, e "funciona num navegador" era uma frase que se dizia. O HTML Standard acabou com isso ao especificar o próprio parser, incluindo cada recuperação de cada erro: "The error handling for parse errors is well-defined (that's the processing rules described throughout this specification)". Desde então há uma árvore, a mesma em todo navegador conforme, e ela pode ser mostrada. O explicador de estrutura HTML e DOM a mostra, construída pelo parse5, o mesmo parser que o jsdom e o Cheerio executam, com cada reparo marcado onde o parser o fez. Este artigo é o guia de campo dessas marcas. A especificação foi lida em 2026-10-05 e é citada como está.

Uma árvore sempre sai

A posição da especificação cabe numa frase: "Parse errors are only errors with the syntax of HTML." Um erro é um ponto no algoritmo, não um veredito sobre o documento. Em cada um desses pontos a especificação diz o que o parser faz em seguida, e os verificadores de conformidade "must report at least one parse error condition to the user if one or more parse error conditions exist in the document". Cinquenta e dois desses pontos têm códigos dedicados numa tabela (13.2.2), com nomes como duplicate-attribute, eof-in-tag e non-void-html-element-start-tag-with-trailing-solidus; as descrições são não normativas, mas exatas: "This error occurs if the parser encounters an attribute in a tag that already has an attribute with the same name. The parser ignores all such duplicate occurrences of the attribute." Os erros da construção da árvore (uma tag de fechamento perdida, elementos ainda abertos no fim do arquivo, um segundo body) são descritos em prosa, sem código, e um parser que os relata os nomeia por conta própria.

É por isso que "view source" e "inspecionar elemento" mostram mundos diferentes. O código é o que você escreveu; o inspetor é a árvore que o parser construiu. Toda diferença entre os dois é um de um punhado de reparos, e cada um tem nome.

Os cinco reparos

Elementos que o parser cria. Um documento sempre tem html, head e body, tenham ou não as tags sido escritas: "Omitting an element's start tag in the situations described below does not mean the element is not present; it is implied, but it is still there." Uma table com linhas mas sem tbody ganha um tbody. No explicador esses elementos não carregam número de linha, porque não têm posição no código; um ponto fica na coluna em seu lugar.

Tags de fechamento que o parser supre. Dezenove elementos podem legalmente ficar abertos: a seção de tags opcionais da especificação lista html, head, body, li, dt, dd, p, rt, rp, optgroup, option, colgroup, caption, thead, tbody, tfoot, tr, td e th, cada um com a sua condição. Para p: "A p element's end tag may be omitted if the p element is immediately followed by an address, article, aside, blockquote, details, dialog, div, dl, fieldset, figcaption, figure, footer, form, h1, h2, h3, h4, h5, h6, header, hgroup, hr, main, menu, nav, ol, p, pre, search, section, table, or ul element, or if there is no more content in the parent element". Então <p>Olá<p>Mundo são dois parágrafos completos, por desenho. Mas <div><span>texto</div> também fecha o span; o parser supre a tag de fechamento ali também, e para um span isso não é sancionado por nenhuma regra. O explicador desenha a mesma marca nos dois casos, e o seu achado D13 nomeia os elementos cuja tag de fechamento suprida significa uma tag que faltou ou ficou fora de ordem.

Elementos de formatação que o parser divide. <p>a<b>b<i>c</b>d</i> é o caso clássico. Quando </b> chega, i é o elemento aberto mais interno, não b. O adoption agency algorithm da especificação fecha b e, porque i estava aberto dentro dele e precisa continuar aberto para o d que segue, reabre uma cópia de i depois de b. Um <i> no código vira dois elementos i na árvore, ambos com a mesma posição inicial. O resultado renderiza como o autor provavelmente queria, que é exatamente por que ninguém percebe até que um estilo ou um script se dirija ao "elemento i" e encontre dois.

Conteúdo que o parser tira de dentro de uma tabela. Dentro de uma table só podem aparecer partes de tabela: caption, colgroup, thead, tbody, tfoot, tr, td, th. Texto ou um elemento escrito diretamente dentro da tabela, <table><tr><td>x</td></tr>perdido</table>, não pode entrar na tabela, então a regra de foster parenting da especificação o insere antes dela. Na árvore, "perdido" precede a tabela dentro da qual foi escrito; na renderização aparece acima dela, que é onde as pessoas começam a procurar o defeito.

Erros de digitação que o parser transforma em comentários ou atributos. <!--> é "an empty comment that is abruptly closed"; <![CDATA[...]]> fora de SVG ou MathML é tratado como comentário; <?xml version="1.0"?> em HTML também vira comentário. <div id="a"class="b"> é lido como se houvesse um espaço (missing-whitespace-between-attributes). <div/> não fecha o div: "The trailing U+002F (/) in a start tag name can be used only in foreign content to specify self-closing tags. (Self-closing tags don't exist in HTML.) It is also allowed for void elements, but doesn't have any effect in this case." Nos treze elementos vazios (area, base, br, col, embed, hr, img, input, link, meta, source, track, wbr) a barra é "unnecessary and has no effect of any kind"; em qualquer outro é um erro e o elemento engole o que vem depois.

O DOCTYPE e os dois modos

Tudo isso acontece dentro de um modo. O parser da especificação começa no modo de inserção "initial" esperando um DOCTYPE, e se qualquer outra coisa chega primeiro, "this is a parse error; if the parser cannot change the mode flag is false, set the Document to quirks mode." A especificação explica o que está em jogo sem eufemismo: "A DOCTYPE is a required preamble. DOCTYPEs are required for legacy reasons. When omitted, browsers tend to use a different rendering mode that is incompatible with some specifications." O quirks mode é a emulação dos navegadores anteriores aos padrões; um DOCTYPE legado com identificador de sistema dá o limited-quirks mode, quase o padrão com alguns comportamentos de células de tabela e imagens inline mantidos. <!DOCTYPE html> são os quinze caracteres que encerram a questão.

Por que a WCAG 2.2 pôde abandonar o 4.1.1

A WCAG 2.0 e a 2.1 tinham um critério de sucesso chamado Parsing, 4.1.1, nível A: "In content implemented using markup languages, elements have complete start and end tags, elements are nested according to their specifications, elements do not contain duplicate attributes, and any IDs are unique, except where the specifications allow these features." Ele existia porque as tecnologias assistivas um dia analisavam o HTML por conta própria e quebravam com marcação que os navegadores sobreviviam. A edição atual da WCAG 2.1 (W3C Recommendation, 06 de maio de 2025) mantém a frase mas acrescenta uma nota que a esvazia: "This success criterion should be considered as always satisfied for any content using HTML or XML", porque o HTML Living Standard agora "requires that user agents support handling these cases consistently", e "in practice, this criterion no longer provides any benefit to people with disabilities in itself." A WCAG 2.2 o removeu, e o seu documento Understanding diz por quê: "Since WCAG 2.0 was published, the specifications (such as HTML) and browsers have improved their handling of parsing errors. It is also the case that assistive technology used to do their own parsing of markup, but now rely on the browser." Toda tecnologia assistiva agora lê a única árvore que o parser da especificação constrói. A régua não se moveu; mudou a coisa medida. O que continua importando é a estrutura que essa árvore carrega, e é aí que vivem os outros critérios: 1.1.1 Non-text Content ("All non-text content that is presented to the user has a text alternative that serves the equivalent purpose"), 1.3.1 Info and Relationships ("Information, structure, and relationships conveyed through presentation can be programmatically determined or are available in text"), 2.4.2 Page Titled, 2.4.6 Headings and Labels, 3.1.1 Language of Page ("The default human language of each web page can be programmatically determined"). Um i mal aninhado não falha em nenhum deles; um título que salta de h1 para h3 falha no 1.3.1 e na regra do próprio HTML Standard de que o nível de cada título seja "less than, equal to, or 1 greater than" o anterior; um img sem alt falha no 1.1.1 e no "the alt attribute must be specified and its value must not be empty" da especificação. Os achados do explicador são essas frases, aplicadas à árvore que de fato saiu.

Fontes