Compruebe si una cadena es html o no

98

Tengo una determinada cadena para la que quiero comprobar si es un html o no. Estoy usando expresiones regulares para lo mismo pero no obtengo el resultado adecuado.

Validé mi expresión regular y funciona bien aquí .

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

Aquí está el violín, pero la expresión regular no se está ejecutando allí. http://jsfiddle.net/wFWtc/

En mi máquina, el código funciona bien pero obtengo un falso en lugar de verdadero como resultado. ¿Qué me falta aquí?

usuario1240679
fuente
5
Utilice un analizador de HTML para analizar HTML. Lea esto si aún no lo ha hecho.
Frédéric Hamidi
3
la pregunta sigue viniendo, debería haber un bot de pila que automáticamente establecerá un comentario en cada pregunta con html y regex en él
Bartlomiej Lewandowski
3
Depende un poco del nivel de sofisticación que desee del cheque. Puede verificar si la cadena contiene al menos uno <y al menos uno >y llamarlo HTML, o puede verificar que sea estrictamente válido con la sintaxis HTML correcta, o cualquier cosa intermedia. Para los casos más simples, no es necesario un analizador HTML.
JJJ
2
¿Por qué verifica que una cadena sea HTML?
nhahtdh
2
@ user1240679: ¿formato de marcado válido? ¿Qué tipo de validez? En el sentido más estricto, necesita DTD para describirlo. En un sentido general, es posible que desee verificar que las etiquetas coincidan correctamente. Cualquiera de los 2 casos anteriores no es trabajo para expresiones regulares.
nhahtdh

Respuestas:

315

Una expresión regular mejor para usar para verificar si una cadena es HTML es:

/^/

Por ejemplo:

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

De hecho, es tan bueno que regresará truepor cada cadena que se le pase, lo cual se debe a que cada cadena es HTML . En serio, incluso si tiene un formato deficiente o no es válido, sigue siendo HTML.

Si lo que está buscando es la presencia de elementos HTML, en lugar de simplemente cualquier contenido de texto, puede usar algo como:

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

No lo ayudará a analizar el HTML de ninguna manera, pero ciertamente marcará la cadena como que contiene elementos HTML.

zzzzBov
fuente
47
Sinceramente, estoy sorprendido de no haber obtenido más votos negativos por el sarcasmo.
zzzzBov
7
@clenemt, ¿entonces lo consideras a < b && a > cHTML?
zzzzBov
1
@zzzzBov, sabes que consideras a<b && a>cHTML ... Desearía que la detección de HTML se simplificara tanto. Analizar nunca es fácil.
Oriadam
2
@oriadam, el contexto era para detectar elementos en ese caso. Si utiliza a < b && a > cel navegador a su vez, el >y <caracteres en &gt;y &lt;entidades apropiadamente. Si, en cambio, utiliza a<b && a>cel navegador interpretará el marcado como a<b && a>c</b>porque la falta de un espacio significa que <babre un <b>elemento. Aquí hay una demostración rápida de lo que estoy hablando .
zzzzBov
4
Esta es probablemente la respuesta de troll más votada que he visto. ;)
aandis
72

Método # 1 . Aquí está la función simple para probar si la cadena contiene datos 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;
}

La idea es permitir que el analizador DOM del navegador decida si la cadena proporcionada se parece a un HTML o no. Como puede ver, simplemente busca ELEMENT_NODE( nodeTypede 1).

Hice un par de pruebas y 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 solución detectará correctamente la cadena HTML, sin embargo, tiene un efecto secundario que img / vide / etc. Las etiquetas comenzarán a descargar el recurso una vez analizadas en innerHTML.

Método # 2 . Otro método usa DOMParser y no tiene efectos secundarios de recursos de carga:

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.fromes el método ES2015, se puede reemplazar por [].slice.call(doc.body.childNodes).
2. La función de flecha en la somellamada se puede reemplazar con la función anónima habitual.

dfsq
fuente
3
Esta es una idea genial. Sin embargo, esta función no pudo detectar la etiqueta de cierre (es decir isHTML("</a>") --> false).
Lewis
9
¡Gran solución! ... El único efecto secundario negativo de es que si su html contiene recursos estáticos como un atributo src de imagen ... innerHTMLforzará al navegador a comenzar a buscar esos recursos. :(
Jose Browne
@JoseBrowne incluso si no está adjunto al DOM?
Kuus
1
@kuus Sí, incluso si no se agrega. Utilice la solución DOMParser.
dfsq
1
Buena idea, pero ¿no sería mejor la respuesta aceptada para el rendimiento? Especialmente si tiene cadenas enormes (juego de palabras) o si tiene que usar mucho esta prueba.
DerpyNerd
13

Un poco de validación con:

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

Esto busca etiquetas vacías (algunas predefinidas) y /etiquetas vacías XHTML terminadas y valida como HTML debido a la etiqueta vacía O capturará el nombre de la etiqueta e intentará encontrar su etiqueta de cierre en algún lugar de la cadena para validarla como HTML.

Demostración explicada: http://regex101.com/r/cX0eP2

Actualizar:

Validación completa con:

/<(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) 

Esto hace una validación adecuada ya que contiene TODAS las etiquetas HTML, primero las vacías y luego las demás que necesitan una etiqueta de cierre.

Demostración explicada aquí: http://regex101.com/r/pE1mT5

CSᵠ
fuente
1
Solo una nota: la expresión regular inferior funciona, pero no detectará etiquetas html no cerradas como "'<strong> hola mundo". dado que este es un html roto, por lo tanto, debe tratarse como una cadena, pero para fines prácticos, es posible que su aplicación también desee detectarlos.
TK123
HTML está diseñado con el perdón de los agentes de usuario en mente. Las etiquetas "no válidas" no son válidas, simplemente son desconocidas y están permitidas. Los atributos "no válidos" no son inválidos ... Esto es particularmente notable cuando uno comienza a involucrar "componentes web" y tecnologías como JSX, que mezclan HTML y descripciones de componentes más ricas, generando típicamente DOM en la sombra. Coloque esto en un archivo y evalúelo document.querySelector('strange'): funcionará.
amcgregor
(Para resumir: debido a cómo está escrita la especificación, intentar "validar" el marcado HTML es esencialmente una tarea tonta. El vínculo que se proporciona a un documento HTML de muestra con un elemento "no válido", allí, es un 100% completamente formado, documento HTML completo, y lo ha sido desde 1997, como otro ejemplo.)
amcgregor
9

La respuesta de zzzzBov anterior es buena, pero no tiene en cuenta las etiquetas de cierre perdidas, como por ejemplo:

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

Una versión que también captura etiquetas de cierre podría ser esta:

/<[a-z/][\s\S]*>/i.test('foo </b> bar'); // true
AeonOfTime
fuente
Podría haber sido mejor sugerir una edición, en lugar de publicar esto como un comentario.
Zlatin Zlatev
Creo que te refieres a <[a-z/][\s\S]*>... nota la barra en el primer grupo.
Ryan Guill
7

Aquí hay una frase descuidada que uso de vez en cuando:

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

Básicamente, regresará truepara cadenas que contengan un <seguido de ANYTHINGseguido de >.

Con ANYTHING, me refiero básicamente a cualquier cosa excepto a una cadena vacía.

No es genial, pero es una frase.

Uso

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

Como puede ver, está lejos de ser perfecto, pero podría hacer el trabajo por usted en algunos casos.

Johan Dettmar
fuente
1
justo lo que necesitaba. Nada lujoso, solo limpio. ¡Gracias!
moeiscool
6

Todas las respuestas aquí son demasiado inclusivas, solo buscan <seguido de >. No existe una forma perfecta de detectar si una cadena es HTML, pero puede hacerlo mejor.

A continuación, buscamos etiquetas finales y serán mucho más ajustadas y precisas:

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

Y aquí está en acción:

# 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ón de velocidad
fuente
4

Si está creando una expresión regular a partir de un literal de cadena, debe escapar de las barras invertidas:

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

Esto no es necesario si usa un literal de expresión regular, pero luego debe escapar de las barras diagonales:

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

Además, su jsfiddle no funcionó porque asignó un onloadcontrolador dentro de otro onloadcontrolador; el valor predeterminado establecido en el panel Frameworks & Extensions a la izquierda es envolver el JS en un archivo onload. Cambie eso a una opción nowrap y corrija el escape literal de la cadena y "funciona" (dentro de las restricciones que todos han señalado en los comentarios): http://jsfiddle.net/wFWtc/4/

Hasta donde yo sé, las expresiones regulares de JavaScript no tienen referencias inversas. Entonces esta parte de tu expresión:

</\1>

no funcionará en JS (pero funcionaría en algunos otros idiomas).

nnnnnn
fuente
Bueno, esto probará que una de las etiquetas se ve bien, pero nada sobre el resto. No estoy seguro de qué tipo de "validez" quiere ese OP.
nhahtdh
1
¿qué pasa con <br> <hr> <input...>@ user1240679?
CSᵠ
3

/<\/?[^>]*>/.test(str) Solo detecta si contiene etiquetas html, puede ser un xml

brillar
fuente
27 is < 42, and 96 > 42. Esto no es HTML.
amcgregor hace
3

Con jQuery:

function isHTML(str) {
  return /^<.*?>$/.test(str) && !!$(str)[0];
}
gtournie
fuente
2
isHTML("<foo>");// devuelve verdadero isHTML("div");// devuelve verdadero si hay divs en la página
ACK_stoverflow
@yekta - ¿De qué estás hablando? Se supone que esto verifica si la cadena es html o no. Un correo electrónico no es una etiqueta html que yo sepa ... isHTML ('[email protected] ') -> false // correct
gtournie
1
Una cadena puede ser cualquier cosa, si sabe que es una etiqueta HTML, entonces, ¿por qué verificar si es HTML en primer lugar? No sigo su punto. No @es una sintaxis válida para un selector. Por lo tanto, cuando lo pasa a un selector de jQuery, lanzará una excepción (es decir, $("[email protected]")desde !!$(str)[0]). Me refiero específicamente a la !!$(str)[0] porción. Acaba de editar su respuesta, pero ahora está comprobando HTML antes de que jQuery haga algo.
yekta
No creo que el autor quisiera comprobar si era solo una cadena. Ese es el punto. Lo que quería era una función capaz de verificar si la cadena era una etiqueta HTML válida , no solo HTML (de lo contrario, esto es un poco estúpido). Actualicé mi respuesta después de leer el comentario de @ACK_stoverflow, pero estoy seguro de que una simple expresión regular debería hacerlo.
gtournie
3

Usando jQuery en este caso, la forma más simple sería:

if ($(testString).length > 0)

Si $(testString).length = 1, esto significa que hay una etiqueta HTML dentro textStging.

Christo Peev
fuente
De acuerdo con la respuesta que se encuentra a continuación (comenzando con "Con jQuery", escrito cuatro años antes de este), considere la mala elección de múltiples usos desde un solo punto de entrada. $()es una operación de selector de CSS. Pero también una fábrica de nodos DOM a partir de la serialización HTML textual. Pero también ... según la otra respuesta que sufre la misma dependencia de jQuery, "div" no es HTML, pero eso devolvería truesi <div>existe algún elemento en la página. Este es un enfoque muy, muy malo, como he llegado a esperar con casi cualquier solución que involucre innecesariamente a jQuery. (Déjalo morir.)
amcgregor
1

Existen soluciones sofisticadas que implican la utilización del navegador para intentar analizar el texto, identificando si se construyeron nodos DOM, lo que será… lento. O expresiones regulares que serán más rápidas, pero ... potencialmente inexactas. También hay dos preguntas muy distintas que surgen de este problema:

P1: ¿Una cadena contiene fragmentos HTML?

¿La cadena es parte de un documento HTML, que contiene marcas de elementos HTML o entidades codificadas? Esto se puede usar como un indicador de que la cadena puede requerir blanqueo / desinfección o decodificación de entidades:

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

Puede ver este patrón en uso en todos los ejemplos de todas las respuestas existentes en el momento de escribir este artículo, además de algunos ... bastante espantosos textos de muestra generados en WYSIWYG o Word y una variedad de referencias de entidades de caracteres.

P2: ¿La cadena es un documento HTML?

La especificación HTML es sorprendentemente flexible en cuanto a lo que considera un documento HTML . Los navegadores hacen todo lo posible para analizar casi cualquier texto basura como HTML. Dos enfoques: simplemente considere todo HTML (ya que si se entrega con un tipo de text/htmlcontenido, el usuario-agente hará un gran esfuerzo para tratar de interpretarlo como HTML) o busque el marcador de prefijo:

<!DOCTYPE html>

En términos de "buena formación", eso y casi nada más se "requiere". El siguiente es un documento HTML 100% completo y totalmente válido que contiene todos los elementos HTML que cree que se están omitiendo:

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

Sip. Hay reglas explícitas sobre cómo formar "faltantes" elementos tales como <html>, <head>, y <body>. Aunque me parece bastante divertido que el resaltado de sintaxis de SO no haya podido detectarlo correctamente sin una pista explícita.

amcgregor
fuente
0

Mi solucion es

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
fuente
Su expresión regular parece muy defectuosa en comparación con una expresión más completa , y es muy desafortunado requerir un procesamiento previo (el reemplazo inicial).
amcgregor
-1

Hay un paquete de NPM is-html que puede intentar resolver este https://github.com/sindresorhus/is-html

Colin D
fuente
No comprendo la expresión que está intentando usar que falla excepto en el tipo de documento declarado, y el patrón "completo" construido a partir de elementos HTML conocidos extraídos de una dependencia adicional ignora el hecho de que no es así como funciona HTML y no lo ha hecho ha sido durante mucho, mucho tiempo. Además, el patrón base menciona explícitamente <html>y <body>etiquetas, las cuales son completamente opcionales . La prueba "no coincide con XML" es reveladora.
amcgregor
@amcgregor, si cree que su solución es mejor, ¿quizás contribuya al repositorio de isHTML? y agregar su conjunto de pruebas de regex101? sería valioso para la comunidad
Colin D
El propósito fundamental de esa biblioteca está mal orientado y será inherentemente incorrecto en un gran número de casos, generalmente por señalización falsa como no HTML debido a la presencia de etiquetas que no comprende; la validación no puede tener éxito de esta manera. Además, una simple expresión regular o un (editar: par de ) bibliotecas ... es posible que hayamos olvidado cómo programar , y Node / NPM no es un lenguaje o cadena de herramientas que generalmente deseo utilizar, contribuir o fomentar el uso de .
amcgregor
Muy bien amcgergor, estás siendo bastante negativo conmigo cuando solo estaba tratando de ayudar. No estoy de acuerdo con la premisa de que npm está equivocada. Imagine que su respuesta de desbordamiento de pila surgió con un pequeño ajuste en el futuro. Yo, como desarrollador que usa su biblioteca, simplemente actualizaría y obtendría un comportamiento más adecuado. En cambio, tengo que ... vivir con el comportamiento roto o volver a visitar esta respuesta de desbordamiento de pila para obtener sus ediciones. Ese es el universo alternativo
Colin D
¿Negativo? Estaba explicando mi postura y por qué no estaría haciendo lo que de otra manera parecería algo sensato. Sin embargo, tenga en cuenta que el artículo que vinculé fue la continuación de un primero un poco más inflamatorio (vinculado al principio ) que generó mucha discusión. Publicó un artículo técnico , también vinculado allí, hacia la parte inferior. Contrarresto tu instinto de reelaboración con pruebas sobre la calidad. Ref: §7.2 (y el desastre de la almohadilla izquierda y eslint)
amcgregor