Verifique se uma string é html ou não

98

Eu tenho uma determinada string para a qual quero verificar se é um html ou não. Estou usando regex para o mesmo, mas não obtive o resultado adequado.

Eu validei meu regex e ele funciona bem aqui .

var htmlRegex = new RegExp("<([A-Za-z][A-Za-z0-9]*)\b[^>]*>(.*?)</\1>");
return htmlRegex.test(testString);

Aqui está o violino, mas o regex não está sendo executado lá. http://jsfiddle.net/wFWtc/

Na minha máquina, o código funciona bem, mas obtenho um falso em vez de verdadeiro como resultado. O que estou faltando aqui?

user1240679
fonte
5
Use um analisador HTML para analisar HTML. Leia isto se ainda não o fez.
Frédéric Hamidi
3
a questão continua chegando, deve haver um bot de pilha que definirá automaticamente um comentário em cada questão com html e regex nele
Bartlomiej Lewandowski
3
Isso depende do nível de sofisticação que você deseja do cheque. Você pode verificar se a string contém pelo menos um <e pelo menos um >e chamá-la de HTML ou pode verificar se ela é estritamente válida com a sintaxe HTML correta ou qualquer coisa entre os dois. Para os casos mais simples, um analisador HTML não é necessário.
JJJ
2
Por que você verifica se uma string é HTML?
nhahtdh
2
@ user1240679: Formato de marcação válido? Que tipo de validade? No sentido mais estrito, você precisa do DTD para descrevê-lo. Em um sentido mais amplo, você pode querer verificar se as tags estão combinadas corretamente. Qualquer um dos 2 casos acima não é tarefa para regex.
nhahtdh

Respostas:

315

A melhor regex a ser usada para verificar se uma string é HTML é:

/^/

Por exemplo:

/^/.test('') // true
/^/.test('foo bar baz') //true
/^/.test('<p>fizz buzz</p>') //true

Na verdade, é tão bom que retornará truepara cada string passada para ele, porque cada string é HTML . Sério, mesmo que seja mal formatado ou inválido, ainda é HTML.

Se o que você está procurando é a presença de elementos HTML, em vez de simplesmente qualquer conteúdo de texto, você pode usar algo como:

/<\/?[a-z][\s\S]*>/i.test()

Não ajudará você a analisar o HTML de nenhuma forma, mas certamente sinalizará a string como contendo elementos HTML.

zzzzBov
fonte
47
Estou sinceramente surpreso por não ter recebido mais votos negativos para o snark.
zzzzBov
7
@clenemt, então você considera a < b && a > cser HTML?
zzzzBov
1
@zzzzBov você sabe que considera a<b && a>cHTML ... Gostaria que a detecção de HTML pudesse ser simplificada tanto. Analisar nunca é fácil.
Oriadam
2
@oriadam, o contexto era para detectar elementos naquele caso. Se você usa a < b && a > co navegador irá transformar o >e <caracteres em &gt;e &lt;entidades de forma adequada. Se, em vez disso, você usar a<b && a>co navegador interpretará a marcação como a<b && a>c</b>porque a falta de um espaço significa que <babre um <b>elemento. Aqui está uma demonstração rápida do que estou falando .
zzzzBov
4
Esta é provavelmente a resposta troll mais votada que já vi. ;)
aandis de
72

Método # 1 . Esta é a função simples para testar se a string contém dados HTML:

function isHTML(str) {
  var a = document.createElement('div');
  a.innerHTML = str;

  for (var c = a.childNodes, i = c.length; i--; ) {
    if (c[i].nodeType == 1) return true; 
  }

  return false;
}

A ideia é permitir que o analisador DOM do navegador decida se a string fornecida se parece com um HTML ou não. Como você pode ver, ele simplesmente verifica ELEMENT_NODE( nodeTypede 1).

Fiz alguns testes e parece que funciona:

isHTML('<a>this is a string</a>') // true
isHTML('this is a string')        // false
isHTML('this is a <b>string</b>') // true

Esta solução detectará corretamente a string HTML, no entanto, tem o efeito colateral de img / vide / etc. as tags começarão a baixar o recurso depois de analisadas no innerHTML.

Método # 2 . Outro método usa DOMParser e não tem efeitos colaterais de recursos de carregamento:

function isHTML(str) {
  var doc = new DOMParser().parseFromString(str, "text/html");
  return Array.from(doc.body.childNodes).some(node => node.nodeType === 1);
}

Notas:
1. Array.fromé o método ES2015, pode ser substituído por [].slice.call(doc.body.childNodes).
2. A função de seta na somechamada pode ser substituída pela função anônima usual.

dfsq
fonte
3
Esta é uma ideia incrível. No entanto, esta função não conseguiu detectar a tag de fechamento (ou seja isHTML("</a>") --> false).
Lewis,
9
Ótima solução! .. O único efeito colateral negativo é que se o seu html contiver quaisquer recursos estáticos como um atributo src de imagem .. innerHTMLforçará o navegador a começar a buscar esses recursos. :(
Jose Browne
@JoseBrowne, mesmo que não esteja anexado ao DOM?
kuus
1
@kuus Sim, mesmo que não seja anexado. Use a solução DOMParser.
dfsq
1
Boa ideia, mas a resposta aceita não seria melhor para o desempenho? Especialmente se você tiver strings grandes (trocadilho intencional) ou se você tiver que usar muito este teste.
DerpyNerd
13

Um pouco de validação com:

/<(?=.*? .*?\/ ?>|br|hr|input|!--|wbr)[a-z]+.*?>|<([a-z]+).*?<\/\1>/i.test(htmlStringHere) 

Isso procura por tags vazias (algumas predefinidas) e /tags vazias XHTML encerradas e valida como HTML por causa da tag vazia OU irá capturar o nome da tag e tentar encontrar sua tag de fechamento em algum lugar na string para validar como HTML.

Demonstração explicada: http://regex101.com/r/cX0eP2

Atualizar:

Validação completa com:

/<(br|basefont|hr|input|source|frame|param|area|meta|!--|col|link|option|base|img|wbr|!DOCTYPE).*?>|<(a|abbr|acronym|address|applet|article|aside|audio|b|bdi|bdo|big|blockquote|body|button|canvas|caption|center|cite|code|colgroup|command|datalist|dd|del|details|dfn|dialog|dir|div|dl|dt|em|embed|fieldset|figcaption|figure|font|footer|form|frameset|head|header|hgroup|h1|h2|h3|h4|h5|h6|html|i|iframe|ins|kbd|keygen|label|legend|li|map|mark|menu|meter|nav|noframes|noscript|object|ol|optgroup|output|p|pre|progress|q|rp|rt|ruby|s|samp|script|section|select|small|span|strike|strong|style|sub|summary|sup|table|tbody|td|textarea|tfoot|th|thead|time|title|tr|track|tt|u|ul|var|video).*?<\/\2>/i.test(htmlStringHere) 

Isso faz a validação adequada , pois contém TODAS as tags HTML, as vazias primeiro, seguidas das demais que precisam de uma tag de fechamento.

Demonstração explicada aqui: http://regex101.com/r/pE1mT5

CSᵠ
fonte
1
Apenas uma observação: o regex inferior funciona, mas não detecta tags html não fechadas, como "'<strong> hello world". concedido este é um html quebrado, portanto, deve ser tratado como uma string, mas para fins práticos, seu aplicativo pode querer detectá-los também.
TK123 de
O HTML foi desenvolvido pensando no perdão dos usuários-agentes. Tags "inválidas" não são inválidas, são apenas desconhecidas e permitidas. Atributos "inválidos" não são inválidos ... Isso é particularmente notável quando se começa a envolver "componentes da web" e tecnologias como JSX, que misturam HTML e descrições de componentes mais ricas, geralmente gerando DOM de sombra. Coloque isso em um arquivo e avalie document.querySelector('strange')- vai funcionar.
amcgregor
(Para resumir: devido à forma como a especificação é escrita, tentar "validar" a marcação HTML é essencialmente um erro. O link fornecido para um documento HTML de amostra com um elemento "inválido", ali, é 100% totalmente formado, documento HTML completo - e tem sido desde 1997 - como outro exemplo.)
amcgregor
9

A resposta do zzzzBov acima é boa, mas não leva em conta as tags de fechamento perdidas, como por exemplo:

/<[a-z][\s\S]*>/i.test('foo </b> bar'); // false

Uma versão que também captura as tags de fechamento pode ser esta:

/<[a-z/][\s\S]*>/i.test('foo </b> bar'); // true
AeonOfTime
fonte
Poderia ter sido melhor sugerir uma edição, em vez de postar como um comentário.
Zlatin Zlatev
Eu acho que você quer dizer <[a-z/][\s\S]*>- observe a barra no primeiro grupo.
Ryan Guill
7

Aqui está uma linha simples que uso de vez em quando:

var isHTML = RegExp.prototype.test.bind(/(<([^>]+)>)/i);

Basicamente, ele retornará truepara strings contendo a <seguido de ANYTHINGseguido de >.

Por ANYTHING, quero dizer basicamente qualquer coisa, exceto uma string vazia.

Não é ótimo, mas é uma linha única.

Uso

isHTML('Testing');               // false
isHTML('<p>Testing</p>');        // true
isHTML('<img src="hello.jpg">'); // true
isHTML('My < weird > string');   // true (caution!!!)
isHTML('<>');                    // false

Como você pode ver, está longe de ser perfeito, mas pode fazer o trabalho para você em alguns casos.

Johan Dettmar
fonte
1
apenas o que eu precisava. Nada extravagante, apenas limpo. Obrigado!
moeiscool
6

Todas as respostas aqui são excessivamente inclusivas, elas apenas procuram <seguido por >. Não existe uma maneira perfeita de detectar se uma string é HTML, mas você pode fazer melhor.

Abaixo, procuramos as tags finais e serão muito mais rígidas e precisas:

import re
re_is_html = re.compile(r"(?:</[^<]+>)|(?:<[^<]+/>)")

E aqui está ele em ação:

# Correctly identified as not HTML:
print re_is_html.search("Hello, World")
print re_is_html.search("This is less than <, this is greater than >.")
print re_is_html.search(" a < 3 && b > 3")
print re_is_html.search("<<Important Text>>")
print re_is_html.search("<a>")

# Correctly identified as HTML
print re_is_html.search("<a>Foo</a>")
print re_is_html.search("<input type='submit' value='Ok' />")
print re_is_html.search("<br/>")

# We don't handle, but could with more tweaking:
print re_is_html.search("<br>")
print re_is_html.search("Foo &amp; bar")
print re_is_html.search("<input type='submit' value='Ok'>")
avião rápido
fonte
4

Se você estiver criando uma regex a partir de um literal de string, será necessário escapar de qualquer barra invertida:

var htmlRegex = new RegExp("<([A-Za-z][A-Za-z0-9]*)\\b[^>]*>(.*?)</\\1>");
// extra backslash added here ---------------------^ and here -----^

Isso não é necessário se você usar um literal regex, mas precisa escapar das barras:

var htmlRegex = /<([A-Za-z][A-Za-z0-9]*)\b[^>]*>(.*?)<\/\1>/;
// forward slash escaped here ------------------------^

Além disso, seu jsfiddle não funcionou porque você atribuiu um onloadmanipulador dentro de outro onloadmanipulador - o padrão, conforme definido no painel Frameworks e Extensões à esquerda é envolver o JS em um onload. Mude isso para uma opção nowrap e corrija o escape literal da string e isso "funciona" (dentro das restrições que todos apontaram nos comentários): http://jsfiddle.net/wFWtc/4/

Pelo que eu sei, as expressões regulares do JavaScript não têm referências anteriores. Portanto, esta parte da sua expressão:

</\1>

não funcionará em JS (mas funcionará em algumas outras linguagens).

nnnnnn
fonte
Bem, isso testará se uma das tags parece OK, mas nada sobre o resto. Não tenho certeza do tipo de "validade" que o OP deseja.
nhahtdh
1
e <br> <hr> <input...>@ user1240679?
CSᵠ
3

/<\/?[^>]*>/.test(str) Detecte apenas se ele contém tags html, pode ser um xml

brilhar
fonte
27 is < 42, and 96 > 42. Isso não é HTML.
amcgregor
3

Com jQuery:

function isHTML(str) {
  return /^<.*?>$/.test(str) && !!$(str)[0];
}
gtournie
fonte
2
isHTML("<foo>");// retorna verdadeiro isHTML("div");// retorna verdadeiro se houver divs na página
ACK_stoverflow
@yekta - Do que você está falando? Isso deve verificar se a string é html ou não. Um e-mail não é uma tag html até onde eu sei ... isHTML ('[email protected] ') -> false // correto
gtournie
1
Uma string pode ser qualquer coisa, se você sabe que é uma tag HTML, então por que verificar se é HTML em primeiro lugar, não estou entendendo bem o seu ponto. Não @é uma sintaxe válida para um seletor. Portanto, quando você passa para um seletor jQuery, ele lançará uma exceção (ou seja, $("[email protected]")de !!$(str)[0]). Estou me referindo especificamente à !!$(str)[0] porção. Você acabou de editar sua resposta, mas agora está verificando o HTML antes que o jQuery faça qualquer coisa.
yekta de
Não creio que o autor quisesse verificar se era apenas um barbante. Essa é a questão. O que ele queria era uma função capaz de verificar se a string era uma tag HTML válida , não apenas HTML (caso contrário, isso é um pouco estúpido). Atualizei minha resposta depois de ler o comentário @ACK_stoverflow, mas tenho certeza de que um regex simples deve bastar.
gtournie
3

Usando jQuery neste caso, a forma mais simples seria:

if ($(testString).length > 0)

Se $(testString).length = 1, isso significa que há uma tag HTML dentro textStging.

Christo Peev
fonte
De acordo com a resposta logo abaixo (começando com "Com jQuery", escrito quatro anos antes deste!), Considere a má escolha de vários usos a partir de um único ponto de entrada. $()é uma operação de seletor CSS. Mas também uma fábrica de nós DOM da serialização de HTML textual. Mas também ... de acordo com a outra resposta que sofre da mesma dependência do jQuery, "div" não é HTML, mas retornaria truese algum <div>elemento existisse na página. Essa é uma abordagem muito, muito ruim, como eu já esperava com quase todas as soluções que envolvem jQuery desnecessariamente. (Deixe morrer.)
Amcgregor
1

Existem soluções sofisticadas que envolvem a utilização do próprio navegador para tentar analisar o texto, identificando se algum nó DOM foi construído, o que será ... lento. Ou expressões regulares que serão mais rápidas, mas ... potencialmente imprecisas. Existem também duas questões muito distintas decorrentes deste problema:

Q1: uma string contém fragmentos HTML?

A string é parte de um documento HTML, contendo marcação de elemento HTML ou entidades codificadas? Isso pode ser usado como um indicador de que a string pode exigir branqueamento / higienização ou decodificação de entidade:

/</?[a-z][^>]*>|(\&(?:[\w\d]+|#\d+|#x[a-f\d]+);/

Você pode ver esse padrão em uso em todos os exemplos de todas as respostas existentes no momento em que este livro foi escrito, além de alguns ... hediondos textos de amostra gerados por WYSIWYG ou Word e uma variedade de referências a entidades de caracteres.

P2: A string é um documento HTML?

A especificação HTML é chocantemente frouxa quanto ao que considera um documento HTML . Os navegadores vão a extremos para analisar quase qualquer texto lixo como HTML. Duas abordagens: ou apenas considerar tudo HTML (já que se entregue com um text/htmlContent-Type, grande esforço será despendido para tentar interpretá-lo como HTML pelo agente do usuário) ou procurar o marcador de prefixo:

<!DOCTYPE html>

Em termos de "boa formação", isso e quase nada mais é "necessário". A seguir está um documento HTML 100% completo e totalmente válido, contendo todos os elementos HTML que você acha que estão sendo omitidos:

<!DOCTYPE html>
<title>Yes, really.</title>
<p>This is everything you need.

Sim. Existem regras explícitas sobre como formar elementos "perdidos", como <html>, <head>, e <body>. Embora eu ache bastante divertido que o realce de sintaxe do SO não tenha detectado isso corretamente sem uma dica explícita.

Amcgregor
fonte
0

Minha solução é

const element = document.querySelector('.test_element');

const setHtml = elem =>{
    let getElemContent = elem.innerHTML;

    // Clean Up whitespace in the element
    // If you don't want to remove whitespace, then you can skip this line
    let newHtml = getElemContent.replace(/[\n\t ]+/g, " ");

    //RegEX to check HTML
    let checkHtml = /<([A-Za-z][A-Za-z0-9]*)\b[^>]*>(.*?)<\/\1>/.test(getElemContent);

    //Check it is html or not
    if (checkHtml){
        console.log('This is an HTML');
        console.log(newHtml.trim());
    }
    else{
        console.log('This is a TEXT');
        console.log(elem.innerText.trim());
    }
}

setHtml(element);
Kamrujaman Shohel
fonte
Sua expressão regular parece altamente defeituosa em comparação com uma expressão mais abrangente , e exigir pré-processamento (a substituição inicial) é altamente lamentável.
amcgregor
-1

Existe um pacote NPM is-html que pode tentar resolver este https://github.com/sindresorhus/is-html

Colin D
fonte
Eu não compreendo a expressão que ele está tentando usar, que falha exceto no doctype declarado, e o padrão "completo" construído a partir de elementos HTML conhecidos puxados de uma dependência adicional ignora o fato de que não é assim que o HTML funciona, e não há muito, muito tempo. Além disso, o padrão base menciona explicitamente <html>e <body>tags, sendo que ambos são totalmente opcionais . O teste "não corresponde a XML" é revelador.
amcgregor
@amcgregor se você acha que sua solução é melhor, talvez contribua para o repositório isHTML? e adicionar seu conjunto de testes de regex101? seria valioso para a comunidade
Colin D
O propósito fundamental dessa biblioteca é equivocado e estará inerentemente errado em um grande número de casos, geralmente por sinalização falsa como não HTML devido à presença de tags que ela não entende; a validação não pode ter sucesso desta forma. Além disso, um regex simples ou um (editar: par de ) bibliotecários ... podemos ter esquecido como programar , e o Node / NPM não é uma linguagem ou conjunto de ferramentas que geralmente desejo utilizar, contribuir ou encorajar o uso de .
amcgregor
Tudo bem, amcgergor, você está sendo muito negativo comigo quando eu estava apenas tentando ajudar. Não concordo com a premissa de que o npm está equivocado. Imagine que sua resposta para estouro de pilha surgiu com um pequeno ajuste no futuro. Eu, como um desenvolvedor usando sua biblioteca, iria apenas atualizar e obter um comportamento mais adequado. Em vez disso, eu tenho que ... conviver com o comportamento quebrado ou revisitar esta resposta de estouro de pilha para obter suas edições? Esse é o universo alternativo
Colin D
Negativo? Eu estava explicando minha postura e por que não estaria fazendo o que de outra forma pareceria uma coisa sensata. Observe, no entanto, que o artigo que vinculei foi a continuação de um primeiro um pouco mais inflamatório (vinculado no início) que gerou muita discussão. Ele publicou um artigo técnico , também vinculado lá, no final. Eu combato sua intuição sobre retrabalho com evidências sobre qualidade. Ref: § 7.2 (e o desastre do painel esquerdo e eslint)
amcgregor